diff --git a/DEPS b/DEPS
index 136e8ba..105fa2d 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'f47962efbb797d21e50e9d8e2b74a7e03817c327',
+  'v8_revision': 'b61ed8b277be1677ef8fa5fe90d42b32bf9539fc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc b/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
index 2fd3d3f..66d9e291 100644
--- a/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
+++ b/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
@@ -342,7 +342,8 @@
       show_keyboard_(false),
       force_show_keyboard_(false),
       should_block_shelf_auto_hide_(false),
-      keyboard_suppressed_(false) {
+      keyboard_suppressed_(false),
+      show_bubble_after_keyboard_hidden_(false) {
   if (MaterialDesignController::IsShelfMaterial()) {
     SetInkDropMode(InkDropMode::ON);
     SetContentsBackground(false);
@@ -365,6 +366,19 @@
 }
 
 void ImeMenuTray::ShowImeMenuBubble() {
+  keyboard::KeyboardController* keyboard_controller =
+      keyboard::KeyboardController::GetInstance();
+  if (keyboard_controller && keyboard_controller->keyboard_visible()) {
+    show_bubble_after_keyboard_hidden_ = true;
+    keyboard_controller->AddObserver(this);
+    keyboard_controller->HideKeyboard(
+        keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
+  } else {
+    ShowImeMenuBubbleInternal();
+  }
+}
+
+void ImeMenuTray::ShowImeMenuBubbleInternal() {
   int minimum_menu_width = GetMinimumMenuWidth();
   should_block_shelf_auto_hide_ = true;
   views::TrayBubbleView::InitParams init_params(
@@ -554,6 +568,17 @@
 }
 
 void ImeMenuTray::OnKeyboardHidden() {
+  if (show_bubble_after_keyboard_hidden_) {
+    show_bubble_after_keyboard_hidden_ = false;
+    keyboard::KeyboardController* keyboard_controller =
+        keyboard::KeyboardController::GetInstance();
+    if (keyboard_controller)
+      keyboard_controller->RemoveObserver(this);
+
+    ShowImeMenuBubbleInternal();
+    return;
+  }
+
   if (!show_keyboard_)
     return;
 
diff --git a/ash/common/system/chromeos/ime_menu/ime_menu_tray.h b/ash/common/system/chromeos/ime_menu/ime_menu_tray.h
index 4360c43..0c83552 100644
--- a/ash/common/system/chromeos/ime_menu/ime_menu_tray.h
+++ b/ash/common/system/chromeos/ime_menu/ime_menu_tray.h
@@ -92,6 +92,9 @@
   // To allow the test class to access |label_|.
   friend class ImeMenuTrayTest;
 
+  // Show the IME menu bubble immediately.
+  void ShowImeMenuBubbleInternal();
+
   // Updates the text of the label on the tray.
   void UpdateTrayLabel();
 
@@ -105,6 +108,7 @@
   bool force_show_keyboard_;
   bool should_block_shelf_auto_hide_;
   bool keyboard_suppressed_;
+  bool show_bubble_after_keyboard_hidden_;
 
   DISALLOW_COPY_AND_ASSIGN(ImeMenuTray);
 };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java
index 3746766..a68bde6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandler.java
@@ -9,7 +9,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
-import android.os.SystemClock;
 
 import org.chromium.base.ContextUtils;
 
@@ -26,7 +25,6 @@
     private final Runnable mUnregisterTask;
 
     private Intent mDeferredIntent;
-    private long mDeferredIntentCreatedTime;
     private boolean mReceiverRegistered;
 
     public DelayedScreenLockIntentHandler() {
@@ -64,7 +62,6 @@
         }
 
         mDeferredIntent = intent;
-        mDeferredIntentCreatedTime = SystemClock.elapsedRealtime();
         registerReceiver();
         mTaskHandler.postDelayed(mUnregisterTask, VALID_DEFERRED_PERIOD_MS);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
index bc74595..52ba312 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
@@ -10,6 +10,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
+import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
@@ -216,6 +217,7 @@
         if (manager == null) return false;
 
         return !cvc.getWebContents().isIncognito()
+                && FirstRunStatus.getFirstRunFlowComplete()
                 && !PrefServiceBridge.getInstance().isContextualSearchDisabled()
                 && TemplateUrlService.getInstance().isDefaultSearchEngineGoogle()
                 // Svelte and Accessibility devices are incompatible with the first-run flow and
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 4ee4ddf..d43e2f8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -13,6 +13,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.graphics.Point;
 import android.os.SystemClock;
 import android.support.test.filters.SmallTest;
@@ -46,6 +47,7 @@
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchQuickActionControl;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFakeServer.FakeSlowResolveSearch;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler;
+import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.browser.gsa.GSAContextDisplaySelection;
 import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.tab.Tab;
@@ -120,6 +122,12 @@
         // We have to set up the test server before starting the activity.
         mTestServer = EmbeddedTestServer.createAndStartServer(getInstrumentation().getContext());
 
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                FirstRunStatus.setFirstRunFlowComplete(true);
+            }
+        });
         super.setUp();
 
         mManager = getActivity().getContextualSearchManager();
@@ -151,6 +159,12 @@
     @Override
     protected void tearDown() throws Exception {
         mTestServer.stopAndDestroyServer();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                FirstRunStatus.setFirstRunFlowComplete(false);
+            }
+        });
         super.tearDown();
     }
 
@@ -971,7 +985,12 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                ContextUtils.getAppSharedPreferences().edit().clear().apply();
+                SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+                boolean freStatus = prefs.getBoolean(FirstRunStatus.FIRST_RUN_FLOW_COMPLETE, false);
+                prefs.edit()
+                        .clear()
+                        .putBoolean(FirstRunStatus.FIRST_RUN_FLOW_COMPLETE, freStatus)
+                        .apply();
             }
         });
     }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 37486eba..9bab239 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5155,7 +5155,13 @@
         "No-State Prefetch" pre-downloads resources to improve load times. "Prerender" does a full pre-rendering of the page, to improve load times even more. "Simple Load" does nothing and is similar to disabling the feature, but collects more metrics for comparison purposes.
       </message>
 
-      <!-- Print scaling feature -->
+      <!-- Print Preview features -->
+      <message name="IDS_FLAGS_PRINT_PDF_AS_IMAGE_NAME" desc="Name for the flag to add the option to print PDFs as images to print preview.">
+        Print Pdf as Image
+      </message>
+      <message name="IDS_FLAGS_PRINT_PDF_AS_IMAGE_DESCRIPTION" desc="Description for the flag to add the option to print PDFs as images in print preview.">
+        If enabled, an option to print PDF files as images will be available in print preview.
+      </message>
       <message name="IDS_FLAGS_PRINT_SCALING_NAME" desc="Name for the flag to add the print scaling feature to print preview.">
         Print Scaling.
       </message>
@@ -8639,6 +8645,9 @@
         <message name="IDS_PRINT_PREVIEW_OPTION_SELECTION_ONLY" desc="Checkbox label shown in print preview page to print only selected content.">
           Selection only
         </message>
+        <message name="IDS_PRINT_PREVIEW_OPTION_RASTERIZE" desc="Checkbox label shown in print preview page to print PDF as an image (rasterize PDF).">
+          Print as image
+        </message>
         <message name="IDS_PRINT_PREVIEW_MARGINS_LABEL" desc="Margins option label. Provides user the option to change the margins of the printed page.">
           Margins
         </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 231dba5..cc98c5c 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2658,14 +2658,17 @@
     <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_TEXT" desc="Text explaining that this setting is used to adjust the boundaries of the selected display.">
       Adjust the boundaries of your desktop within the display
     </message>
-    <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_TITLE" desc="Name of the settings subpage which adjusts display overscan.">
+    <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_TITLE" desc="Title of the settings subpage which adjusts display overscan.">
       Overscan
     </message>
-    <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_INSTRUCTIONS" desc="Instructions for changing the overscan calibration.">
-      Use arrow keys to adjust picture size and alignment.
+    <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_SUBTITLE" desc="Subtitle for the display overscan settings subpage.">
+      Adjust the boundaries of your display.
+    </message>
+    <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_INSTRUCTIONS" desc="Instructions for changing the display overscan calibration.">
+      Tap the following keys to adjust or move the cropping area.
     </message>
     <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_RESIZE" desc="Label for resizing overscan calibration.">
-      Shrink/expand
+      Shrink / Expand
     </message>
     <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_POSITION" desc="Label for positioning overscan calibration.">
       Move
@@ -2673,9 +2676,6 @@
     <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_RESET" desc="Label for resetting overscan calibration.">
       Reset
     </message>
-    <message name="IDS_SETTINGS_DISPLAY_OVERSCAN_SAVE" desc="Label for saving overscan calibration.">
-      Save
-    </message>
     <message name="IDS_SETTINGS_DISPLAY_MIRRORING_ON" desc="Label indicating that the displays are currently mirrored">
       On
     </message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4afb92b..dada3f3 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2074,6 +2074,9 @@
          features::kDisplayPersistenceToggleInPermissionPrompts)},
 #endif
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+    {"print-pdf-as-image", IDS_FLAGS_PRINT_PDF_AS_IMAGE_NAME,
+      IDS_FLAGS_PRINT_PDF_AS_IMAGE_DESCRIPTION, kOsDesktop,
+      FEATURE_VALUE_TYPE(features::kPrintPdfAsImage)},
     {"print-scaling", IDS_FLAGS_PRINT_SCALING_NAME,
      IDS_FLAGS_PRINT_SCALING_DESCRIPTION, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kPrintScaling)},
diff --git a/chrome/browser/resources/print_preview/data/print_ticket_store.js b/chrome/browser/resources/print_preview/data/print_ticket_store.js
index c3abdf28..007419c 100644
--- a/chrome/browser/resources/print_preview/data/print_ticket_store.js
+++ b/chrome/browser/resources/print_preview/data/print_ticket_store.js
@@ -110,6 +110,14 @@
         new print_preview.ticket_items.PageRange(this.documentInfo_);
 
     /**
+     * Rasterize PDF ticket item.
+     * @type {!print_preview.ticket_items.Rasterize}
+     * @private
+     */
+    this.rasterize_ = new print_preview.ticket_items.Rasterize(
+        this.destinationStore_, this.documentInfo_);
+
+    /**
      * Scaling ticket item.
      * @type {!print_preview.ticket_items.Scaling}
      * @private
@@ -291,6 +299,10 @@
       return this.pageRange_;
     },
 
+    get rasterize() {
+      return this.rasterize_;
+    },
+
     get scaling() {
       return this.scaling_;
     },
diff --git a/chrome/browser/resources/print_preview/data/ticket_items/rasterize.js b/chrome/browser/resources/print_preview/data/ticket_items/rasterize.js
new file mode 100644
index 0000000..b8ea06d
--- /dev/null
+++ b/chrome/browser/resources/print_preview/data/ticket_items/rasterize.js
@@ -0,0 +1,50 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('print_preview.ticket_items', function() {
+  'use strict';
+
+  /**
+   * Rasterize ticket item whose value is a {@code boolean} that indicates
+   * whether the PDF document should be rendered as images.
+   * @constructor
+   * @param {!print_preview.DocumentInfo} documentInfo Information about the
+   *     document to print, used to determine if document is a PDF.
+   * @extends {print_preview.ticket_items.TicketItem}
+   */
+  function Rasterize(destinationStore, documentInfo) {
+    print_preview.ticket_items.TicketItem.call(
+        this, null /* appState */, null /* field */,
+        null /* destinationStore */, documentInfo);
+  };
+
+  Rasterize.prototype = {
+    __proto__: print_preview.ticket_items.TicketItem.prototype,
+
+    /** @override */
+    wouldValueBeValid: function(value) {
+      return true;
+    },
+
+    /** @override */
+    isCapabilityAvailable: function() {
+      return !this.getDocumentInfoInternal().isModifiable;
+    },
+
+    /** @override */
+    getDefaultValueInternal: function() {
+      return false;
+    },
+
+    /** @override */
+    getCapabilityNotAvailableValueInternal: function() {
+      return this.getDefaultValueInternal();
+    }
+  };
+
+  // Export
+  return {
+    Rasterize: Rasterize
+  };
+});
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index c242f890..e0c54bc3 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -288,6 +288,7 @@
         'generateDraftData': documentInfo.isModifiable,
         'fitToPageEnabled': printTicketStore.fitToPage.getValue(),
         'scaleFactor': printTicketStore.scaling.getValueAsNumber(),
+        'rasterizePDF': printTicketStore.rasterize.getValue(),
         // NOTE: Even though the following fields don't directly relate to the
         // preview, they still need to be included.
         'duplex': printTicketStore.duplex.getValue() ?
@@ -367,6 +368,7 @@
         'printWithCloudPrint': !destination.isLocal,
         'printWithPrivet': destination.isPrivet,
         'printWithExtension': destination.isExtension,
+        'rasterizePDF': printTicketStore.rasterize.getValue(),
         'scaleFactor': printTicketStore.scaling.getValueAsNumber(),
         'deviceName': destination.id,
         'isFirstRequest': false,
diff --git a/chrome/browser/resources/print_preview/preview_generator.js b/chrome/browser/resources/print_preview/preview_generator.js
index 8313ed73..39c6d697 100644
--- a/chrome/browser/resources/print_preview/preview_generator.js
+++ b/chrome/browser/resources/print_preview/preview_generator.js
@@ -86,6 +86,13 @@
     this.colorValue_ = false;
 
     /**
+     * Whether the document should be printed as a raster PDF.
+     * @type {boolean}
+     * @private
+     */
+    this.rasterizeValue_ = false;
+
+    /**
      * Whether the document should be fitted to the page.
      * @type {boolean}
      * @private
@@ -184,6 +191,7 @@
       this.isHeaderFooterEnabled_ =
           this.printTicketStore_.headerFooter.getValue();
       this.colorValue_ = this.printTicketStore_.color.getValue();
+      this.rasterizeValue_ = this.printTicketStore_.rasterize.getValue();
       this.isFitToPageEnabled_ = this.printTicketStore_.fitToPage.getValue();
       this.scalingValue_ = this.printTicketStore_.scaling.getValueAsNumber();
       this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
@@ -284,6 +292,7 @@
           !ticketStore.landscape.isValueEqual(this.isLandscapeEnabled_) ||
           !ticketStore.headerFooter.isValueEqual(this.isHeaderFooterEnabled_) ||
           !ticketStore.color.isValueEqual(this.colorValue_) ||
+          !ticketStore.rasterize.isValueEqual(this.rasterizeValue_) ||
           !ticketStore.scaling.isValueEqual(this.scalingValue_) ||
           !ticketStore.fitToPage.isValueEqual(this.isFitToPageEnabled_) ||
           this.pageRanges_ == null ||
diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chrome/browser/resources/print_preview/previewarea/preview_area.js
index 3c25bc0..ca553a7c 100644
--- a/chrome/browser/resources/print_preview/previewarea/preview_area.js
+++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js
@@ -337,6 +337,7 @@
         this.printTicketStore_.landscape,
         this.printTicketStore_.marginsType,
         this.printTicketStore_.pageRange,
+        this.printTicketStore_.rasterize,
         this.printTicketStore_.selectionOnly,
         this.printTicketStore_.scaling
       ].forEach(function(setting) {
diff --git a/chrome/browser/resources/print_preview/print_preview.js b/chrome/browser/resources/print_preview/print_preview.js
index afe40161..93b272cd 100644
--- a/chrome/browser/resources/print_preview/print_preview.js
+++ b/chrome/browser/resources/print_preview/print_preview.js
@@ -188,7 +188,8 @@
         this.printTicketStore_.fitToPage,
         this.printTicketStore_.cssBackground,
         this.printTicketStore_.selectionOnly,
-        this.printTicketStore_.headerFooter);
+        this.printTicketStore_.headerFooter,
+        this.printTicketStore_.rasterize);
     this.addChild(this.otherOptionsSettings_);
 
     /**
@@ -1322,6 +1323,7 @@
 <include src="data/ticket_items/fit_to_page.js">
 <include src="data/ticket_items/css_background.js">
 <include src="data/ticket_items/selection_only.js">
+<include src="data/ticket_items/rasterize.js">
 <include src="data/ticket_items/vendor_items.js">
 
 <include src="native_layer.js">
diff --git a/chrome/browser/resources/print_preview/settings/other_options_settings.html b/chrome/browser/resources/print_preview/settings/other_options_settings.html
index f981b25..0514368 100644
--- a/chrome/browser/resources/print_preview/settings/other_options_settings.html
+++ b/chrome/browser/resources/print_preview/settings/other_options_settings.html
@@ -29,6 +29,12 @@
           <span>$i18n{optionBackgroundColorsAndImages}</span>
         </label>
       </div>
+      <div id="rasterize-container">
+        <label aria-live="polite">
+          <input class="checkbox" type="checkbox">
+          <span>$i18n{optionRasterize}</span>
+        </label>
+      </div>
       <div id="selection-only-container">
         <label aria-live="polite">
           <input class="checkbox" type="checkbox">
diff --git a/chrome/browser/resources/print_preview/settings/other_options_settings.js b/chrome/browser/resources/print_preview/settings/other_options_settings.js
index 06a2991..522869ae 100644
--- a/chrome/browser/resources/print_preview/settings/other_options_settings.js
+++ b/chrome/browser/resources/print_preview/settings/other_options_settings.js
@@ -127,30 +127,42 @@
    *     only ticket item.
    * @param {!print_preview.ticket_items.HeaderFooter} headerFooter Header
    *     footer ticket item.
+   * @param {!print_preview.ticket_items.Rasterize} rasterize Rasterize ticket
+   *     item.
    * @constructor
    * @extends {print_preview.SettingsSection}
    */
   function OtherOptionsSettings(
-      duplex, fitToPage, cssBackground, selectionOnly, headerFooter) {
+      duplex, fitToPage, cssBackground, selectionOnly, headerFooter,
+      rasterize) {
     print_preview.SettingsSection.call(this);
+    /**
+     * @private {boolean} rasterizeEnabled Whether the print as image feature is
+     *     enabled.
+     */
+    this.rasterizeEnabled_ = loadTimeData.getBoolean('printPdfAsImageEnabled');
 
-    /*
+    /**
      * @private {!Array<!CheckboxTicketItemElement>} checkbox ticket item
      *      elements representing the different options in the section.
      *      Selection only must always be the last element in the array.
      */
     this.elements_ = [
-        new CheckboxTicketItemElement(headerFooter, true,
-                                      'header-footer-container'),
-        new CheckboxTicketItemElement(fitToPage, false,
-                                      'fit-to-page-container'),
-        new CheckboxTicketItemElement(duplex, false, 'duplex-container'),
-        new CheckboxTicketItemElement(cssBackground, true,
-                                      'css-background-container'),
-        new CheckboxTicketItemElement(selectionOnly, true,
-                                      'selection-only-container')
+      new CheckboxTicketItemElement(headerFooter, true,
+                                    'header-footer-container'),
+      new CheckboxTicketItemElement(fitToPage, false,
+                                    'fit-to-page-container'),
+      new CheckboxTicketItemElement(duplex, false, 'duplex-container'),
+      new CheckboxTicketItemElement(cssBackground, true,
+                                    'css-background-container'),
+      new CheckboxTicketItemElement(selectionOnly, true,
+                                    'selection-only-container')
     ];
-
+    if (this.rasterizeEnabled_) {
+      this.elements_.splice(4, 0,
+                            new CheckboxTicketItemElement(rasterize, true,
+                                'rasterize-container'));
+    }
   };
 
   OtherOptionsSettings.prototype = {
@@ -206,6 +218,7 @@
     decorateInternal: function() {
       for (var i = 0; i < this.elements_.length; i++)
         this.elements_[i].decorate();
+      $('rasterize-container').hidden = !this.rasterizeEnabled_;
     },
 
     /** @override */
diff --git a/chrome/browser/resources/settings/device_page/display_overscan_dialog.html b/chrome/browser/resources/settings/device_page/display_overscan_dialog.html
index 03d7bc8..7b993ce 100644
--- a/chrome/browser/resources/settings/device_page/display_overscan_dialog.html
+++ b/chrome/browser/resources/settings/device_page/display_overscan_dialog.html
@@ -10,21 +10,30 @@
 <dom-module id="settings-display-overscan-dialog">
   <template>
     <style include="settings-shared">
-     .label {
-       margin: 10px 0;
+     .subtitle {
+       margin-top: 10px;
      }
+
+     .instructions {
+       color: var(--paper-grey-600);
+       margin-top: 4px;
+     }
+
      .details {
        margin: 40px;
      }
+
      iron-icon {
        --iron-icon-fill-color: white;
        background: black;
-       margin: 2px;
+       margin: 5px;
      }
+
      #move > div {
        color: grey;
        font-size: 150%;
      }
+
      #move > div.shift {
        background: black;
        color: white;
@@ -36,7 +45,8 @@
     <dialog is="cr-dialog" id="dialog" on-close="close">
       <div class="title">$i18n{displayOverscanPageTitle}</div>
       <div class="body">
-        <div class="label self-start" >$i18n{displayOverscanInstructions}</div>
+        <div class="subtitle" >$i18n{displayOverscanSubtitle}</div>
+        <div class="instructions" >$i18n{displayOverscanInstructions}</div>
         <div class="details layout horizontal around-justified self-stretch">
           <div class="layout vertical center">
             <div class="layout horizontal">
@@ -62,11 +72,11 @@
         </div>
       </div>
       <div class="button-container">
-        <paper-button class="secondary-button" on-tap="onResetTap_">
+        <paper-button id="reset" class="secondary-button" on-tap="onResetTap_">
           $i18n{displayOverscanReset}
         </paper-button>
-        <paper-button class="primary-button"  on-tap="onSaveTap_">
-          $i18n{displayOverscanSave}
+        <paper-button class="primary-button" on-tap="onSaveTap_">
+          $i18n{ok}
         </paper-button>
       </div>
     </dialog>
diff --git a/chrome/browser/resources/settings/device_page/display_overscan_dialog.js b/chrome/browser/resources/settings/device_page/display_overscan_dialog.js
index 109301b..e9f245c 100644
--- a/chrome/browser/resources/settings/device_page/display_overscan_dialog.js
+++ b/chrome/browser/resources/settings/device_page/display_overscan_dialog.js
@@ -32,13 +32,17 @@
 
   open: function() {
     this.keyHandler_ = this.handleKeyEvent_.bind(this);
-    this.addEventListener('keydown', this.keyHandler_);
+    // We need to attach the event listener to |window|, not |this| so that
+    // changing focus does not prevent key events from occuring.
+    window.addEventListener('keydown', this.keyHandler_);
     this.comitted_ = false;
     this.$.dialog.showModal();
+    // Don't focus 'reset' by default. 'Tab' will focus 'OK'.
+    this.$$('#reset').blur();
   },
 
   close: function() {
-    this.removeEventListener('keydown', this.keyHandler_);
+    window.removeEventListener('keydown', this.keyHandler_);
 
     this.displayId = '';  // Will trigger displayIdChanged_.
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.cc b/chrome/browser/ui/webui/media_router/media_router_ui.cc
index 452b802..badd5978 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -725,6 +725,10 @@
              : TruncateHost(GetHostFromURL(gurl));
 }
 
+const std::set<MediaCastMode>& MediaRouterUI::cast_modes() const {
+  return cast_modes_;
+}
+
 const std::string& MediaRouterUI::GetRouteProviderExtensionId() const {
   // TODO(crbug.com/597778): remove reference to MediaRouterMojoImpl
   return static_cast<MediaRouterMojoImpl*>(router_)
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.h b/chrome/browser/ui/webui/media_router/media_router_ui.h
index e8e2901c..1f505b2 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.h
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.h
@@ -95,8 +95,9 @@
   // Closes the media router UI.
   void Close();
 
-  // Notifies this instance that the UI has been initialized.
-  void UIInitialized();
+  // Notifies this instance that the UI has been initialized. Marked virtual for
+  // tests.
+  virtual void UIInitialized();
 
   // Requests a route be created from the source mapped to
   // |cast_mode|, to the sink given by |sink_id|.
@@ -127,12 +128,12 @@
                                  MediaCastMode cast_mode);
 
   // Returns true if the cast mode last chosen for the current origin is tab
-  // mirroring.
-  bool UserSelectedTabMirroringForCurrentOrigin() const;
+  // mirroring. Marked virtual for tests.
+  virtual bool UserSelectedTabMirroringForCurrentOrigin() const;
 
   // Records the cast mode selection for the current origin, unless the cast
-  // mode is MediaCastMode::DESKTOP_MIRROR.
-  void RecordCastModeSelection(MediaCastMode cast_mode);
+  // mode is MediaCastMode::DESKTOP_MIRROR. Marked virtual for tests.
+  virtual void RecordCastModeSelection(MediaCastMode cast_mode);
 
   // Returns the hostname of the default source's parent frame URL.
   std::string GetPresentationRequestSourceName() const;
@@ -145,7 +146,8 @@
   const std::vector<MediaRoute::Id>& joinable_route_ids() const {
     return joinable_route_ids_;
   }
-  const std::set<MediaCastMode>& cast_modes() const { return cast_modes_; }
+  // Marked virtual for tests.
+  virtual const std::set<MediaCastMode>& cast_modes() const;
   const std::unordered_map<MediaRoute::Id, MediaCastMode>&
   routes_and_cast_modes() const {
     return routes_and_cast_modes_;
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
index bfd108f..b2cc643 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
@@ -577,6 +577,20 @@
   EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
 }
 
+TEST_F(MediaRouterUITest, RecordCastModeSelectionsInIncognito) {
+  const GURL url = GURL("https://www.example.com/watch?v=AAAA");
+
+  CreateMediaRouterUIForURL(profile()->GetOffTheRecordProfile(), url);
+  EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+  media_router_ui_->RecordCastModeSelection(MediaCastMode::TAB_MIRROR);
+  EXPECT_TRUE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+
+  // Selections recorded in incognito shouldn't be retrieved in the regular
+  // profile.
+  CreateMediaRouterUIForURL(profile(), url);
+  EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+}
+
 TEST_F(MediaRouterUITest, RecordDesktopMirroringCastModeSelection) {
   const GURL url = GURL("https://www.example.com/watch?v=AAAA");
   CreateMediaRouterUIForURL(profile(), url);
@@ -592,4 +606,5 @@
   // Selecting desktop mirroring should not change the recorded preferences.
   EXPECT_TRUE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
 }
+
 }  // namespace media_router
diff --git a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
index 8d8e152..327422dc 100644
--- a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
+++ b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
@@ -59,6 +59,11 @@
   void set_incognito_for_test(bool incognito) { incognito_ = incognito; }
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(MediaRouterWebUIMessageHandlerTest,
+                           RecordCastModeSelection);
+  FRIEND_TEST_ALL_PREFIXES(MediaRouterWebUIMessageHandlerTest,
+                           RetrieveCastModeSelection);
+
   // WebUIMessageHandler implementation.
   void RegisterMessages() override;
 
diff --git a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
index 10abf2e..626961b4 100644
--- a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
@@ -15,6 +15,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::Return;
 using testing::ReturnRef;
 
 namespace media_router {
@@ -30,6 +31,10 @@
       : MediaRouterUI(web_ui) {}
   ~MockMediaRouterUI() {}
 
+  MOCK_METHOD0(UIInitialized, void());
+  MOCK_CONST_METHOD0(UserSelectedTabMirroringForCurrentOrigin, bool());
+  MOCK_METHOD1(RecordCastModeSelection, void(MediaCastMode cast_mode));
+  MOCK_CONST_METHOD0(cast_modes, const std::set<MediaCastMode>&());
   MOCK_CONST_METHOD0(GetRouteProviderExtensionId, const std::string&());
 };
 
@@ -583,4 +588,51 @@
   EXPECT_TRUE(actual_is_blocking);
 }
 
+TEST_F(MediaRouterWebUIMessageHandlerTest, RecordCastModeSelection) {
+  base::ListValue args;
+  args.AppendInteger(MediaCastMode::DEFAULT);
+  EXPECT_CALL(*mock_media_router_ui_.get(),
+              RecordCastModeSelection(MediaCastMode::DEFAULT))
+      .Times(1);
+  handler_->OnReportSelectedCastMode(&args);
+
+  args.Clear();
+  args.AppendInteger(MediaCastMode::TAB_MIRROR);
+  EXPECT_CALL(*mock_media_router_ui_.get(),
+              RecordCastModeSelection(MediaCastMode::TAB_MIRROR))
+      .Times(1);
+  handler_->OnReportSelectedCastMode(&args);
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, RetrieveCastModeSelection) {
+  base::ListValue args;
+  std::set<MediaCastMode> cast_modes = {MediaCastMode::TAB_MIRROR};
+  EXPECT_CALL(*mock_media_router_ui_, GetRouteProviderExtensionId())
+      .WillRepeatedly(ReturnRef(provider_extension_id()));
+  EXPECT_CALL(*mock_media_router_ui_, cast_modes())
+      .WillRepeatedly(ReturnRef(cast_modes));
+
+  EXPECT_CALL(*mock_media_router_ui_,
+              UserSelectedTabMirroringForCurrentOrigin())
+      .WillOnce(Return(true));
+  handler_->OnRequestInitialData(&args);
+  const content::TestWebUI::CallData& call_data1 = *web_ui_->call_data()[0];
+  ASSERT_EQ("media_router.ui.setInitialData", call_data1.function_name());
+  const base::DictionaryValue* initial_data = nullptr;
+  ASSERT_TRUE(call_data1.arg1()->GetAsDictionary(&initial_data));
+  bool use_tab_mirroring = false;
+  EXPECT_TRUE(initial_data->GetBoolean("useTabMirroring", &use_tab_mirroring));
+  EXPECT_TRUE(use_tab_mirroring);
+
+  EXPECT_CALL(*mock_media_router_ui_,
+              UserSelectedTabMirroringForCurrentOrigin())
+      .WillOnce(Return(false));
+  handler_->OnRequestInitialData(&args);
+  const content::TestWebUI::CallData& call_data2 = *web_ui_->call_data()[1];
+  ASSERT_EQ("media_router.ui.setInitialData", call_data2.function_name());
+  ASSERT_TRUE(call_data2.arg1()->GetAsDictionary(&initial_data));
+  EXPECT_TRUE(initial_data->GetBoolean("useTabMirroring", &use_tab_mirroring));
+  EXPECT_FALSE(use_tab_mirroring);
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 7a9693961..2561020d 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -142,6 +142,7 @@
   NON_DEFAULT_MARGINS,
   DISTILL_PAGE_UNUSED,
   SCALING,
+  PRINT_AS_IMAGE,
   PRINT_SETTINGS_BUCKET_BOUNDARY
 };
 
@@ -289,6 +290,12 @@
                           &external_preview) && external_preview) {
     ReportPrintSettingHistogram(EXTERNAL_PDF_PREVIEW);
   }
+
+  bool rasterize = false;
+  if (settings.GetBoolean(printing::kSettingRasterizePdf,
+                          &rasterize) && rasterize) {
+    ReportPrintSettingHistogram(PRINT_AS_IMAGE);
+  }
 }
 
 // Callback that stores a PDF file on disk.
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index e1f286a..cc8bbd8 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -278,6 +278,8 @@
       IDS_PRINT_PREVIEW_OPTION_BACKGROUND_COLORS_AND_IMAGES);
   source->AddLocalizedString("optionSelectionOnly",
                              IDS_PRINT_PREVIEW_OPTION_SELECTION_ONLY);
+  source->AddLocalizedString("optionRasterize",
+                             IDS_PRINT_PREVIEW_OPTION_RASTERIZE);
   source->AddLocalizedString("marginsLabel", IDS_PRINT_PREVIEW_MARGINS_LABEL);
   source->AddLocalizedString("defaultMargins",
                              IDS_PRINT_PREVIEW_DEFAULT_MARGINS);
@@ -410,6 +412,10 @@
   bool scaling_enabled = base::FeatureList::IsEnabled(features::kPrintScaling);
   source->AddBoolean("scalingEnabled", scaling_enabled);
 
+  bool print_pdf_as_image_enabled = base::FeatureList::IsEnabled(
+      features::kPrintPdfAsImage);
+  source->AddBoolean("printPdfAsImageEnabled", print_pdf_as_image_enabled);
+
 #if defined(OS_CHROMEOS)
   bool cups_and_md_settings_enabled =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
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 b1c1b5c5..12de100 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
@@ -602,12 +602,12 @@
       {"displayOrientationStandard", IDS_SETTINGS_DISPLAY_ORIENTATION_STANDARD},
       {"displayOverscanPageText", IDS_SETTINGS_DISPLAY_OVERSCAN_TEXT},
       {"displayOverscanPageTitle", IDS_SETTINGS_DISPLAY_OVERSCAN_TITLE},
+      {"displayOverscanSubtitle", IDS_SETTINGS_DISPLAY_OVERSCAN_SUBTITLE},
       {"displayOverscanInstructions",
        IDS_SETTINGS_DISPLAY_OVERSCAN_INSTRUCTIONS},
       {"displayOverscanResize", IDS_SETTINGS_DISPLAY_OVERSCAN_RESIZE},
       {"displayOverscanPosition", IDS_SETTINGS_DISPLAY_OVERSCAN_POSITION},
       {"displayOverscanReset", IDS_SETTINGS_DISPLAY_OVERSCAN_RESET},
-      {"displayOverscanSave", IDS_SETTINGS_DISPLAY_OVERSCAN_SAVE},
   };
   AddLocalizedStringsBulk(html_source, display_strings,
                           arraysize(display_strings));
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 02818f60..164adaa3 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -169,6 +169,9 @@
 
 // Enables the Print Scaling feature in print preview.
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+const base::Feature kPrintPdfAsImage{"PrintPdfAsImage",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kPrintScaling{"PrintScaling",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index eb65e67..a839f87f 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -100,6 +100,8 @@
 #endif
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+extern const base::Feature kPrintPdfAsImage;
+
 extern const base::Feature kPrintScaling;
 #endif
 
diff --git a/chrome/test/data/webui/print_preview.js b/chrome/test/data/webui/print_preview.js
index 947424f..5848d82 100644
--- a/chrome/test/data/webui/print_preview.js
+++ b/chrome/test/data/webui/print_preview.js
@@ -61,12 +61,16 @@
    * @override
    */
   testGenPreamble: function() {
-    // Enable print scaling for tests.
+    // Enable print scaling and print as image for tests.
     GEN('  base::FeatureList::ClearInstanceForTesting();');
     GEN('  std::unique_ptr<base::FeatureList>');
     GEN('      feature_list(new base::FeatureList);');
+    GEN('  char enabled_features[128] = {0};');
+    GEN('  strcpy(enabled_features, features::kPrintScaling.name);');
+    GEN('  strcat(strcat(enabled_features, ","), ');
+    GEN('      features::kPrintPdfAsImage.name);');
     GEN('  feature_list->InitializeFromCommandLine(');
-    GEN('      features::kPrintScaling.name, std::string());');
+    GEN('      enabled_features, std::string());');
     GEN('  base::FeatureList::SetInstance(std::move(feature_list));');
   },
 
@@ -519,7 +523,11 @@
   this.setCapabilities(device);
 
   var otherOptions = $('other-options-settings');
-  checkSectionVisible(otherOptions, false);
+  checkSectionVisible(otherOptions, true);
+  checkElementDisplayed(
+      otherOptions.querySelector('#fit-to-page-container'), false);
+  checkElementDisplayed(
+      otherOptions.querySelector('#rasterize-container'), true);
   checkSectionVisible($('media-size-settings'), false);
   checkSectionVisible($('scaling-settings'), false);
 
@@ -535,6 +543,7 @@
 
   var otherOptions = $('other-options-settings');
   var fitToPage = otherOptions.querySelector('#fit-to-page-container');
+  var rasterize = otherOptions.querySelector('#rasterize-container');
   var mediaSize = $('media-size-settings');
   var scalingSettings = $('scaling-settings');
 
@@ -542,12 +551,14 @@
   // available).
   checkSectionVisible(otherOptions, true);
   checkElementDisplayed(fitToPage, false);
+  checkElementDisplayed(rasterize, false);
   checkSectionVisible(mediaSize, false);
   checkSectionVisible(scalingSettings, false);
 
   this.expandMoreSettings();
 
   checkElementDisplayed(fitToPage, false);
+  checkElementDisplayed(rasterize, false);
   checkSectionVisible(mediaSize, true);
   checkSectionVisible(scalingSettings, true);
 
@@ -566,12 +577,18 @@
   var scalingSettings = $('scaling-settings');
   var fitToPageContainer =
       otherOptions.querySelector('#fit-to-page-container');
+  var rasterizeContainer =
+      otherOptions.querySelector('#rasterize-container');
 
   checkSectionVisible(otherOptions, true);
   checkElementDisplayed(fitToPageContainer, true);
+  checkElementDisplayed(rasterizeContainer, false);
   expectTrue(
       fitToPageContainer.querySelector('.checkbox').checked);
   this.expandMoreSettings();
+  checkElementDisplayed(rasterizeContainer, true);
+  expectFalse(
+      rasterizeContainer.querySelector('.checkbox').checked);
   checkSectionVisible($('media-size-settings'), true);
   checkSectionVisible(scalingSettings, true);
 
diff --git a/components/printing/browser/print_manager_utils.cc b/components/printing/browser/print_manager_utils.cc
index 78112825..ec383a2 100644
--- a/components/printing/browser/print_manager_utils.cc
+++ b/components/printing/browser/print_manager_utils.cc
@@ -25,6 +25,7 @@
   params->scale_factor = settings.scale_factor();
   // Currently hardcoded at 72dpi. See PrintSettings' constructor.
   params->desired_dpi = settings.desired_dpi();
+  params->rasterize_pdf = settings.rasterize_pdf();
   // Always use an invalid cookie.
   params->document_cookie = 0;
   params->selection_only = settings.selection_only();
diff --git a/components/printing/common/print_messages.cc b/components/printing/common/print_messages.cc
index 5820bb1..a9291a2 100644
--- a/components/printing/common/print_messages.cc
+++ b/components/printing/common/print_messages.cc
@@ -42,26 +42,27 @@
 }  // namespace IPC
 
 PrintMsg_Print_Params::PrintMsg_Print_Params()
-  : page_size(),
-    content_size(),
-    printable_area(),
-    margin_top(0),
-    margin_left(0),
-    dpi(0),
-    scale_factor(1.0f),
-    desired_dpi(0),
-    document_cookie(0),
-    selection_only(false),
-    supports_alpha_blend(false),
-    preview_ui_id(-1),
-    preview_request_id(0),
-    is_first_request(false),
-    print_scaling_option(blink::WebPrintScalingOptionSourceSize),
-    print_to_pdf(false),
-    display_header_footer(false),
-    title(),
-    url(),
-    should_print_backgrounds(false) {}
+    : page_size(),
+      content_size(),
+      printable_area(),
+      margin_top(0),
+      margin_left(0),
+      dpi(0),
+      scale_factor(1.0f),
+      desired_dpi(0),
+      rasterize_pdf(false),
+      document_cookie(0),
+      selection_only(false),
+      supports_alpha_blend(false),
+      preview_ui_id(-1),
+      preview_request_id(0),
+      is_first_request(false),
+      print_scaling_option(blink::WebPrintScalingOptionSourceSize),
+      print_to_pdf(false),
+      display_header_footer(false),
+      title(),
+      url(),
+      should_print_backgrounds(false) {}
 
 PrintMsg_Print_Params::PrintMsg_Print_Params(
     const PrintMsg_Print_Params& other) = default;
@@ -77,6 +78,7 @@
   dpi = 0;
   scale_factor = 1.0f;
   desired_dpi = 0;
+  rasterize_pdf = false;
   document_cookie = 0;
   selection_only = false;
   supports_alpha_blend = false;
diff --git a/components/printing/common/print_messages.h b/components/printing/common/print_messages.h
index 2c4fc949..36794997 100644
--- a/components/printing/common/print_messages.h
+++ b/components/printing/common/print_messages.h
@@ -47,6 +47,7 @@
   double dpi;
   double scale_factor;
   int desired_dpi;
+  bool rasterize_pdf;
   int document_cookie;
   bool selection_only;
   bool supports_alpha_blend;
@@ -128,6 +129,9 @@
   // Desired apparent dpi on paper.
   IPC_STRUCT_TRAITS_MEMBER(desired_dpi)
 
+  // Whether to rasterize a PDF for printing
+  IPC_STRUCT_TRAITS_MEMBER(rasterize_pdf)
+
   // Cookie for the document to ensure correctness.
   IPC_STRUCT_TRAITS_MEMBER(document_cookie)
 
diff --git a/components/printing/renderer/print_web_view_helper.cc b/components/printing/renderer/print_web_view_helper.cc
index 07ada41..4abf0ab4 100644
--- a/components/printing/renderer/print_web_view_helper.cc
+++ b/components/printing/renderer/print_web_view_helper.cc
@@ -286,6 +286,7 @@
     blink::WebPrintParams* webkit_print_params) {
   int dpi = GetDPI(&print_params);
   webkit_print_params->printerDPI = dpi;
+  webkit_print_params->rasterizePDF = print_params.rasterize_pdf;
   webkit_print_params->printScalingOption = print_params.print_scaling_option;
 
   webkit_print_params->printContentArea.width = ConvertUnit(
diff --git a/components/toolbar/toolbar_model_impl.cc b/components/toolbar/toolbar_model_impl.cc
index b51addad3..aa569e4a 100644
--- a/components/toolbar/toolbar_model_impl.cc
+++ b/components/toolbar/toolbar_model_impl.cc
@@ -70,7 +70,7 @@
 gfx::VectorIconId ToolbarModelImpl::GetVectorIcon() const {
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
   if (GetURL().SchemeIs("chrome-extension"))
-    return gfx::VectorIconId::EXTENSION;
+    return gfx::VectorIconId::OMNIBOX_EXTENSION_APP;
 
   switch (GetSecurityLevel(false)) {
     case security_state::NONE:
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 2ce23d6..ad2b602 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -1765,23 +1765,31 @@
 }
 
 bool PepperPluginInstanceImpl::GetPreferredPrintOutputFormat(
-    PP_PrintOutputFormat_Dev* format) {
+    PP_PrintOutputFormat_Dev* format,
+    const WebPrintParams& print_params) {
   // Keep a reference on the stack. See NOTE above.
   scoped_refptr<PepperPluginInstanceImpl> ref(this);
   if (!LoadPrintInterface())
     return false;
   uint32_t supported_formats =
       plugin_print_interface_->QuerySupportedFormats(pp_instance());
-  if (supported_formats & PP_PRINTOUTPUTFORMAT_PDF) {
+  if ((supported_formats & PP_PRINTOUTPUTFORMAT_PDF) &&
+      !print_params.rasterizePDF) {
     *format = PP_PRINTOUTPUTFORMAT_PDF;
     return true;
   }
+  if (supported_formats & PP_PRINTOUTPUTFORMAT_RASTER) {
+    *format = PP_PRINTOUTPUTFORMAT_RASTER;
+    return true;
+  }
   return false;
 }
 
 bool PepperPluginInstanceImpl::SupportsPrintInterface() {
   PP_PrintOutputFormat_Dev format;
-  return GetPreferredPrintOutputFormat(&format);
+  WebPrintParams params;
+  params.rasterizePDF = false;
+  return GetPreferredPrintOutputFormat(&format, params);
 }
 
 bool PepperPluginInstanceImpl::IsPrintScalingDisabled() {
@@ -1795,7 +1803,7 @@
   // Keep a reference on the stack. See NOTE above.
   scoped_refptr<PepperPluginInstanceImpl> ref(this);
   PP_PrintOutputFormat_Dev format;
-  if (!GetPreferredPrintOutputFormat(&format)) {
+  if (!GetPreferredPrintOutputFormat(&format, print_params)) {
     // PrintBegin should not have been called since SupportsPrintInterface
     // would have returned false;
     NOTREACHED();
@@ -1857,7 +1865,8 @@
   if (!print_output)
     return;
 
-  if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF)
+  if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF ||
+      current_print_settings_.format == PP_PRINTOUTPUTFORMAT_RASTER)
     PrintPDFOutput(print_output, metafile);
 
   // Now we need to release the print output resource.
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h
index 5125c2c..ada181f 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.h
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.h
@@ -647,7 +647,8 @@
   // Queries the plugin for supported print formats and sets |format| to the
   // best format to use. Returns false if the plugin does not support any
   // print format that we can handle (we can handle only PDF).
-  bool GetPreferredPrintOutputFormat(PP_PrintOutputFormat_Dev* format);
+  bool GetPreferredPrintOutputFormat(PP_PrintOutputFormat_Dev* format,
+                                     const blink::WebPrintParams& params);
   bool PrintPDFOutput(PP_Resource print_output,
                       printing::PdfMetafileSkia* metafile);
 
diff --git a/device/bluetooth/bluetooth_adapter.cc b/device/bluetooth/bluetooth_adapter.cc
index 382928e2..9153dc5 100644
--- a/device/bluetooth/bluetooth_adapter.cc
+++ b/device/bluetooth/bluetooth_adapter.cc
@@ -9,7 +9,6 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
 #include "build/build_config.h"
 #include "device/bluetooth/bluetooth_common.h"
 #include "device/bluetooth/bluetooth_device.h"
@@ -110,10 +109,8 @@
 
 BluetoothAdapter::ConstDeviceList BluetoothAdapter::GetDevices() const {
   ConstDeviceList devices;
-  for (DevicesMap::const_iterator iter = devices_.begin();
-       iter != devices_.end();
-       ++iter)
-    devices.push_back(iter->second);
+  for (const auto& device : devices_)
+    devices.push_back(device.second.get());
 
   return devices;
 }
@@ -130,9 +127,9 @@
   if (canonicalized_address.empty())
     return nullptr;
 
-  DevicesMap::const_iterator iter = devices_.find(canonicalized_address);
+  auto iter = devices_.find(canonicalized_address);
   if (iter != devices_.end())
-    return iter->second;
+    return iter->second.get();
 
   return nullptr;
 }
@@ -376,8 +373,8 @@
 }
 
 void BluetoothAdapter::RemoveTimedOutDevices() {
-  for (DevicesMap::iterator it = devices_.begin(); it != devices_.end();) {
-    BluetoothDevice* device = it->second;
+  for (auto it = devices_.begin(); it != devices_.end();) {
+    BluetoothDevice* device = it->second.get();
     if (device->IsPaired() || device->IsConnected() ||
         device->IsGattConnected()) {
       ++it;
@@ -398,10 +395,10 @@
     }
 
     VLOG(1) << "Removing device: " << device->GetAddress();
-    DevicesMap::iterator next = it;
+    auto next = it;
     next++;
-    std::unique_ptr<BluetoothDevice> removed_device =
-        devices_.take_and_erase(it);
+    std::unique_ptr<BluetoothDevice> removed_device = std::move(it->second);
+    devices_.erase(it);
     it = next;
 
     for (auto& observer : observers_)
diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h
index 8feb409..4a97d320 100644
--- a/device/bluetooth/bluetooth_adapter.h
+++ b/device/bluetooth/bluetooth_adapter.h
@@ -16,7 +16,6 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
@@ -501,12 +500,12 @@
   friend class BluetoothDiscoverySession;
   friend class BluetoothTestBase;
 
-  typedef base::ScopedPtrHashMap<std::string, std::unique_ptr<BluetoothDevice>>
-      DevicesMap;
-  typedef std::pair<BluetoothDevice::PairingDelegate*, PairingDelegatePriority>
-      PairingDelegatePair;
-  typedef base::Callback<void(UMABluetoothDiscoverySessionOutcome)>
-      DiscoverySessionErrorCallback;
+  using DevicesMap =
+      std::unordered_map<std::string, std::unique_ptr<BluetoothDevice>>;
+  using PairingDelegatePair =
+      std::pair<BluetoothDevice::PairingDelegate*, PairingDelegatePriority>;
+  using DiscoverySessionErrorCallback =
+      base::Callback<void(UMABluetoothDiscoverySessionOutcome)>;
 
   BluetoothAdapter();
   virtual ~BluetoothAdapter();
diff --git a/device/bluetooth/bluetooth_adapter_android.cc b/device/bluetooth/bluetooth_adapter_android.cc
index 41228bb..2155707 100644
--- a/device/bluetooth/bluetooth_adapter_android.cc
+++ b/device/bluetooth/bluetooth_adapter_android.cc
@@ -183,7 +183,7 @@
     const JavaParamRef<jobjectArray>& advertised_uuids,  // Java Type: String[]
     int32_t tx_power) {
   std::string device_address = ConvertJavaStringToUTF8(env, address);
-  DevicesMap::const_iterator iter = devices_.find(device_address);
+  auto iter = devices_.find(device_address);
 
   bool is_new_device = false;
   std::unique_ptr<BluetoothDeviceAndroid> device_android_owner;
@@ -197,7 +197,7 @@
     device_android = device_android_owner.get();
   } else {
     // Existing device.
-    device_android = static_cast<BluetoothDeviceAndroid*>(iter->second);
+    device_android = static_cast<BluetoothDeviceAndroid*>(iter->second.get());
   }
   DCHECK(device_android);
 
@@ -219,7 +219,7 @@
       tx_power == INT32_MIN ? nullptr : &clamped_tx_power);
 
   if (is_new_device) {
-    devices_.add(device_address, std::move(device_android_owner));
+    devices_[device_address] = std::move(device_android_owner);
     for (auto& observer : observers_)
       observer.DeviceAdded(this, device_android);
   } else {
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index 10f7639..ce300811 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -502,7 +502,7 @@
   }
 
   device_classic = new BluetoothClassicDeviceMac(this, device);
-  devices_.set(device_address, base::WrapUnique(device_classic));
+  devices_[device_address] = base::WrapUnique(device_classic);
   VLOG(1) << "Adding new classic device: " << device_classic->GetAddress();
 
   for (auto& observer : observers_)
@@ -566,7 +566,7 @@
   if (is_new_device) {
     std::string device_address =
         BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
-    devices_.add(device_address, std::unique_ptr<BluetoothDevice>(device_mac));
+    devices_[device_address] = base::WrapUnique(device_mac);
     for (auto& observer : observers_)
       observer.DeviceAdded(this, device_mac);
   } else {
@@ -620,8 +620,7 @@
       device_mac = new BluetoothLowEnergyDeviceMac(this, peripheral);
       std::string device_address =
           BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
-      devices_.add(device_address,
-                   std::unique_ptr<BluetoothDevice>(device_mac));
+      devices_[device_address] = base::WrapUnique(device_mac);
       for (auto& observer : observers_) {
         observer.DeviceAdded(this, device_mac);
       }
@@ -694,11 +693,11 @@
 BluetoothAdapterMac::GetBluetoothLowEnergyDeviceMac(CBPeripheral* peripheral) {
   std::string device_address =
       BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
-  DevicesMap::const_iterator iter = devices_.find(device_address);
+  auto iter = devices_.find(device_address);
   if (iter == devices_.end()) {
     return nil;
   }
-  return static_cast<BluetoothLowEnergyDeviceMac*>(iter->second);
+  return static_cast<BluetoothLowEnergyDeviceMac*>(iter->second.get());
 }
 
 bool BluetoothAdapterMac::DoesCollideWithKnownDevice(
diff --git a/device/bluetooth/bluetooth_adapter_mac_unittest.mm b/device/bluetooth/bluetooth_adapter_mac_unittest.mm
index a17dabd..b330b3fa 100644
--- a/device/bluetooth/bluetooth_adapter_mac_unittest.mm
+++ b/device/bluetooth/bluetooth_adapter_mac_unittest.mm
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/test/test_simple_task_runner.h"
 #include "build/build_config.h"
@@ -83,11 +84,6 @@
     return BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
   }
 
-  void AddLowEnergyDevice(BluetoothLowEnergyDeviceMac* device) {
-    adapter_mac_->devices_.set(device->GetAddress(),
-                               std::unique_ptr<BluetoothDevice>(device));
-  }
-
   int NumDevices() { return adapter_mac_->devices_.size(); }
 
   bool DevicePresent(CBPeripheral* peripheral) {
diff --git a/device/bluetooth/bluetooth_adapter_win.cc b/device/bluetooth/bluetooth_adapter_win.cc
index 1bacaac..2a53da3 100644
--- a/device/bluetooth/bluetooth_adapter_win.cc
+++ b/device/bluetooth/bluetooth_adapter_win.cc
@@ -10,6 +10,7 @@
 
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
@@ -234,58 +235,46 @@
   // new list with the list we know of (|devices_|) and raise corresponding
   // DeviceAdded, DeviceRemoved and DeviceChanged events.
 
-  typedef std::set<std::string> DeviceAddressSet;
+  using DeviceAddressSet = std::set<std::string>;
   DeviceAddressSet known_devices;
-  for (DevicesMap::const_iterator iter = devices_.begin();
-       iter != devices_.end();
-       ++iter) {
-    known_devices.insert((*iter).first);
-  }
+  for (const auto& device : devices_)
+    known_devices.insert(device.first);
 
   DeviceAddressSet new_devices;
-  for (ScopedVector<BluetoothTaskManagerWin::DeviceState>::const_iterator iter =
-           devices.begin();
-       iter != devices.end();
-       ++iter) {
+  for (auto iter = devices.begin(); iter != devices.end(); ++iter)
     new_devices.insert((*iter)->address);
-  }
 
-  // Process device removal first
+  // Process device removal first.
   DeviceAddressSet removed_devices =
       base::STLSetDifference<DeviceAddressSet>(known_devices, new_devices);
-  for (DeviceAddressSet::const_iterator iter = removed_devices.begin();
-       iter != removed_devices.end();
-       ++iter) {
-    std::unique_ptr<BluetoothDevice> device_win =
-        devices_.take_and_erase(*iter);
+  for (const auto& device : removed_devices) {
+    auto it = devices_.find(device);
+    std::unique_ptr<BluetoothDevice> device_win = std::move(it->second);
+    devices_.erase(it);
     for (auto& observer : observers_)
       observer.DeviceRemoved(this, device_win.get());
   }
 
-  // Process added and (maybe) changed devices in one pass
+  // Process added and (maybe) changed devices in one pass.
   DeviceAddressSet added_devices =
       base::STLSetDifference<DeviceAddressSet>(new_devices, known_devices);
   DeviceAddressSet changed_devices =
       base::STLSetIntersection<DeviceAddressSet>(known_devices, new_devices);
-  for (ScopedVector<BluetoothTaskManagerWin::DeviceState>::const_iterator iter =
-           devices.begin();
-       iter != devices.end();
-       ++iter) {
+  for (auto iter = devices.begin(); iter != devices.end(); ++iter) {
     BluetoothTaskManagerWin::DeviceState* device_state = (*iter);
     if (added_devices.find(device_state->address) != added_devices.end()) {
       BluetoothDeviceWin* device_win =
           new BluetoothDeviceWin(this, *device_state, ui_task_runner_,
                                  socket_thread_, NULL, net::NetLogSource());
-      devices_.set(device_state->address,
-                   std::unique_ptr<BluetoothDevice>(device_win));
+      devices_[device_state->address] = base::WrapUnique(device_win);
       for (auto& observer : observers_)
         observer.DeviceAdded(this, device_win);
     } else if (changed_devices.find(device_state->address) !=
                changed_devices.end()) {
-      DevicesMap::const_iterator iter = devices_.find(device_state->address);
+      auto iter = devices_.find(device_state->address);
       DCHECK(iter != devices_.end());
       BluetoothDeviceWin* device_win =
-          static_cast<BluetoothDeviceWin*>(iter->second);
+          static_cast<BluetoothDeviceWin*>(iter->second.get());
       if (!device_win->IsEqual(*device_state)) {
         device_win->Update(*device_state);
         for (auto& observer : observers_)
diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc
index d4e072bb..7c56c12 100644
--- a/device/bluetooth/bluetooth_device.cc
+++ b/device/bluetooth/bluetooth_device.cc
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "base/memory/ptr_util.h"
-#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -23,9 +22,14 @@
 
 namespace device {
 
-BluetoothDevice::DeviceUUIDs::DeviceUUIDs() {}
+BluetoothDevice::DeviceUUIDs::DeviceUUIDs() = default;
 
-BluetoothDevice::DeviceUUIDs::~DeviceUUIDs() {}
+BluetoothDevice::DeviceUUIDs::~DeviceUUIDs() = default;
+
+BluetoothDevice::DeviceUUIDs::DeviceUUIDs(const DeviceUUIDs& other) = default;
+
+BluetoothDevice::DeviceUUIDs& BluetoothDevice::DeviceUUIDs::operator=(
+    const DeviceUUIDs& other) = default;
 
 void BluetoothDevice::DeviceUUIDs::ReplaceAdvertisedUUIDs(
     UUIDList new_advertised_uuids) {
@@ -44,9 +48,8 @@
 void BluetoothDevice::DeviceUUIDs::ReplaceServiceUUIDs(
     const GattServiceMap& gatt_services) {
   service_uuids_.clear();
-  for (const auto& gatt_service_pair : gatt_services) {
+  for (const auto& gatt_service_pair : gatt_services)
     service_uuids_.insert(gatt_service_pair.second->GetUUID());
-  }
   UpdateDeviceUUIDs();
 }
 
@@ -371,13 +374,16 @@
     const {
   std::vector<BluetoothRemoteGattService*> services;
   for (const auto& iter : gatt_services_)
-    services.push_back(iter.second);
+    services.push_back(iter.second.get());
   return services;
 }
 
 BluetoothRemoteGattService* BluetoothDevice::GetGattService(
     const std::string& identifier) const {
-  return gatt_services_.get(identifier);
+  auto it = gatt_services_.find(identifier);
+  if (it == gatt_services_.end())
+    return nullptr;
+  return it->second.get();
 }
 
 // static
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index 8102152..2964496 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -16,21 +16,21 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/gtest_prod_util.h"
+#include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "device/bluetooth/bluetooth_common.h"
 #include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "device/bluetooth/bluetooth_uuid.h"
 
 namespace device {
 
 class BluetoothAdapter;
 class BluetoothGattConnection;
-class BluetoothRemoteGattService;
 class BluetoothSocket;
 class BluetoothUUID;
 
@@ -106,8 +106,8 @@
 
   // Mapping from the platform-specific GATT service identifiers to
   // BluetoothRemoteGattService objects.
-  typedef base::ScopedPtrHashMap<std::string,
-                                 std::unique_ptr<BluetoothRemoteGattService>>
+  typedef std::unordered_map<std::string,
+                             std::unique_ptr<BluetoothRemoteGattService>>
       GattServiceMap;
 
   // Interface for negotiating pairing of bluetooth devices.
@@ -579,6 +579,9 @@
     DeviceUUIDs();
     ~DeviceUUIDs();
 
+    DeviceUUIDs(const DeviceUUIDs& other);
+    DeviceUUIDs& operator=(const DeviceUUIDs& other);
+
     // Advertised Service UUIDs functions
     void ReplaceAdvertisedUUIDs(UUIDList new_advertised_uuids);
 
@@ -601,7 +604,7 @@
     BluetoothDevice::UUIDSet device_uuids_;
   };
 
-  BluetoothDevice(BluetoothAdapter* adapter);
+  explicit BluetoothDevice(BluetoothAdapter* adapter);
 
   // Implements platform specific operations to initiate a GATT connection.
   // Subclasses must also call DidConnectGatt, DidFailToConnectGatt, or
@@ -673,6 +676,8 @@
   // Returns a localized string containing the device's bluetooth address and
   // a device type for display when |name_| is empty.
   base::string16 GetAddressWithLocalizedDeviceTypeName() const;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothDevice);
 };
 
 }  // namespace device
diff --git a/device/bluetooth/bluetooth_device_android.cc b/device/bluetooth/bluetooth_device_android.cc
index ce76d20b..c9cde3fa 100644
--- a/device/bluetooth/bluetooth_device_android.cc
+++ b/device/bluetooth/bluetooth_device_android.cc
@@ -246,17 +246,17 @@
   std::string instance_id_string =
       base::android::ConvertJavaStringToUTF8(env, instance_id);
 
-  if (gatt_services_.contains(instance_id_string))
+  if (gatt_services_.find(instance_id_string) != gatt_services_.end())
     return;
 
-  BluetoothDevice::GattServiceMap::iterator service_iterator =
-      gatt_services_.set(
-          instance_id_string,
-          BluetoothRemoteGattServiceAndroid::Create(
-              GetAndroidAdapter(), this, bluetooth_gatt_service_wrapper,
-              instance_id_string, j_device_));
+  std::unique_ptr<BluetoothRemoteGattServiceAndroid> service =
+      BluetoothRemoteGattServiceAndroid::Create(GetAndroidAdapter(), this,
+                                                bluetooth_gatt_service_wrapper,
+                                                instance_id_string, j_device_);
+  BluetoothRemoteGattServiceAndroid* service_ptr = service.get();
+  gatt_services_[instance_id_string] = std::move(service);
 
-  adapter_->NotifyGattServiceAdded(service_iterator->second);
+  adapter_->NotifyGattServiceAdded(service_ptr);
 }
 
 BluetoothDeviceAndroid::BluetoothDeviceAndroid(BluetoothAdapterAndroid* adapter)
diff --git a/device/bluetooth/bluetooth_device_win.cc b/device/bluetooth/bluetooth_device_win.cc
index e628a255..a82eebd 100644
--- a/device/bluetooth/bluetooth_device_win.cc
+++ b/device/bluetooth/bluetooth_device_win.cc
@@ -7,8 +7,8 @@
 #include <memory>
 #include <string>
 
-#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/scoped_vector.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/stringprintf.h"
@@ -51,7 +51,9 @@
     service_keys.push_back(gatt_service.first);
   }
   for (const auto& key : service_keys) {
-    gatt_services_.take_and_erase(key);
+    std::unique_ptr<BluetoothRemoteGattService> service =
+        std::move(gatt_services_[key]);
+    gatt_services_.erase(key);
   }
 }
 
@@ -213,9 +215,8 @@
 
 const BluetoothServiceRecordWin* BluetoothDeviceWin::GetServiceRecord(
     const device::BluetoothUUID& uuid) const {
-  for (ServiceRecordList::const_iterator iter = service_record_list_.begin();
-       iter != service_record_list_.end();
-       ++iter) {
+  for (auto iter = service_record_list_.begin();
+       iter != service_record_list_.end(); ++iter) {
     if ((*iter)->uuid() == uuid)
       return *iter;
   }
@@ -233,21 +234,17 @@
   }
 
   // Checks service collection
-  typedef base::ScopedPtrHashMap<std::string,
-                                 std::unique_ptr<BluetoothServiceRecordWin>>
-      ServiceRecordMap;
-
   UUIDSet new_services;
-  ServiceRecordMap new_service_records;
-  for (ScopedVector<BluetoothTaskManagerWin::ServiceRecordState>::const_iterator
-           iter = device_state.service_record_states.begin();
+  std::unordered_map<std::string, std::unique_ptr<BluetoothServiceRecordWin>>
+      new_service_records;
+  for (auto iter = device_state.service_record_states.begin();
        iter != device_state.service_record_states.end(); ++iter) {
-    BluetoothServiceRecordWin* service_record = new BluetoothServiceRecordWin(
-        address_, (*iter)->name, (*iter)->sdp_bytes, (*iter)->gatt_uuid);
+    std::unique_ptr<BluetoothServiceRecordWin> service_record =
+        base::MakeUnique<BluetoothServiceRecordWin>(
+            address_, (*iter)->name, (*iter)->sdp_bytes, (*iter)->gatt_uuid);
     new_services.insert(service_record->uuid());
-    new_service_records.set(
-        service_record->uuid().canonical_value(),
-        std::unique_ptr<BluetoothServiceRecordWin>(service_record));
+    new_service_records[service_record->uuid().canonical_value()] =
+        std::move(service_record);
   }
 
   // Check that no new services have been added or removed.
@@ -255,11 +252,11 @@
     return false;
   }
 
-  for (ServiceRecordList::const_iterator iter = service_record_list_.begin();
+  for (auto iter = service_record_list_.begin();
        iter != service_record_list_.end(); ++iter) {
     BluetoothServiceRecordWin* service_record = (*iter);
     BluetoothServiceRecordWin* new_service_record =
-        new_service_records.get((*iter)->uuid().canonical_value());
+        new_service_records[(*iter)->uuid().canonical_value()].get();
     if (!service_record->IsEqual(*new_service_record))
       return false;
   }
@@ -300,8 +297,7 @@
   uuids_.clear();
   service_record_list_.clear();
 
-  for (ScopedVector<BluetoothTaskManagerWin::ServiceRecordState>::const_iterator
-           iter = device_state.service_record_states.begin();
+  for (auto iter = device_state.service_record_states.begin();
        iter != device_state.service_record_states.end(); ++iter) {
     BluetoothServiceRecordWin* service_record =
         new BluetoothServiceRecordWin(device_state.address, (*iter)->name,
@@ -316,12 +312,11 @@
 
 bool BluetoothDeviceWin::IsGattServiceDiscovered(BluetoothUUID& uuid,
                                                  uint16_t attribute_handle) {
-  GattServiceMap::iterator it = gatt_services_.begin();
-  for (; it != gatt_services_.end(); it++) {
+  for (const auto& gatt_service : gatt_services_) {
     uint16_t it_att_handle =
-        static_cast<BluetoothRemoteGattServiceWin*>(it->second)
+        static_cast<BluetoothRemoteGattServiceWin*>(gatt_service.second.get())
             ->GetAttributeHandle();
-    BluetoothUUID it_uuid = it->second->GetUUID();
+    BluetoothUUID it_uuid = gatt_service.second->GetUUID();
     if (attribute_handle == it_att_handle && uuid == it_uuid) {
       return true;
     }
@@ -337,8 +332,7 @@
       static_cast<BluetoothRemoteGattServiceWin*>(service)
           ->GetAttributeHandle();
   BluetoothUUID uuid = service->GetUUID();
-  ScopedVector<BluetoothTaskManagerWin::ServiceRecordState>::const_iterator it =
-      service_state.begin();
+  auto it = service_state.begin();
   for (; it != service_state.end(); ++it) {
     if (attribute_handle == (*it)->attribute_handle && uuid == (*it)->gatt_uuid)
       return true;
@@ -353,16 +347,18 @@
   {
     std::vector<std::string> to_be_removed_services;
     for (const auto& gatt_service : gatt_services_) {
-      if (!DoesGattServiceExist(service_state, gatt_service.second)) {
+      if (!DoesGattServiceExist(service_state, gatt_service.second.get())) {
         to_be_removed_services.push_back(gatt_service.first);
       }
     }
     for (const auto& service : to_be_removed_services) {
-      gatt_services_.take_and_erase(service);
+      std::unique_ptr<BluetoothRemoteGattService> service_ptr =
+          std::move(gatt_services_[service]);
+      gatt_services_.erase(service);
     }
     // Update previously discovered services.
-    for (auto gatt_service : gatt_services_) {
-      static_cast<BluetoothRemoteGattServiceWin*>(gatt_service.second)
+    for (const auto& gatt_service : gatt_services_) {
+      static_cast<BluetoothRemoteGattServiceWin*>(gatt_service.second.get())
           ->Update();
     }
   }
@@ -372,17 +368,14 @@
     return;
 
   // Add new services.
-  for (ScopedVector<BluetoothTaskManagerWin::ServiceRecordState>::const_iterator
-           it = service_state.begin();
-       it != service_state.end(); ++it) {
+  for (auto it = service_state.begin(); it != service_state.end(); ++it) {
     if (!IsGattServiceDiscovered((*it)->gatt_uuid, (*it)->attribute_handle)) {
       BluetoothRemoteGattServiceWin* primary_service =
           new BluetoothRemoteGattServiceWin(this, (*it)->path, (*it)->gatt_uuid,
                                             (*it)->attribute_handle, true,
                                             nullptr, ui_task_runner_);
-      gatt_services_.add(
-          primary_service->GetIdentifier(),
-          std::unique_ptr<BluetoothRemoteGattService>(primary_service));
+      gatt_services_[primary_service->GetIdentifier()] =
+          base::WrapUnique(primary_service);
       adapter_->NotifyGattServiceAdded(primary_service);
     }
   }
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
index ba9eeda..00da119 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -206,15 +206,14 @@
     if (!gatt_service) {
       gatt_service = new BluetoothRemoteGattServiceMac(this, cb_service,
                                                        true /* is_primary */);
-      auto result_iter = gatt_services_.add(gatt_service->GetIdentifier(),
-                                            base::WrapUnique(gatt_service));
+      auto result_iter = gatt_services_.insert(std::make_pair(
+          gatt_service->GetIdentifier(), base::WrapUnique(gatt_service)));
       DCHECK(result_iter.second);
       adapter_->NotifyGattServiceAdded(gatt_service);
     }
   }
-  for (GattServiceMap::const_iterator it = gatt_services_.begin();
-       it != gatt_services_.end(); ++it) {
-    device::BluetoothRemoteGattService* gatt_service = it->second;
+  for (auto it = gatt_services_.begin(); it != gatt_services_.end(); ++it) {
+    device::BluetoothRemoteGattService* gatt_service = it->second.get();
     device::BluetoothRemoteGattServiceMac* gatt_service_mac =
         static_cast<BluetoothRemoteGattServiceMac*>(gatt_service);
     gatt_service_mac->DiscoverCharacteristics();
@@ -248,8 +247,8 @@
   bool discovery_complete =
       std::find_if_not(
           gatt_services_.begin(), gatt_services_.end(),
-          [](std::pair<std::string, BluetoothRemoteGattService*> pair) {
-            BluetoothRemoteGattService* gatt_service = pair.second;
+          [](GattServiceMap::value_type& pair) {
+            BluetoothRemoteGattService* gatt_service = pair.second.get();
             return static_cast<BluetoothRemoteGattServiceMac*>(gatt_service)
                 ->IsDiscoveryComplete();
           }) == gatt_services_.end();
@@ -270,7 +269,8 @@
     DCHECK(gatt_service);
     VLOG(1) << gatt_service->GetUUID().canonical_value();
     std::unique_ptr<BluetoothRemoteGattService> scoped_service =
-        gatt_services_.take_and_erase(gatt_service->GetIdentifier());
+        std::move(gatt_services_[gatt_service->GetIdentifier()]);
+    gatt_services_.erase(gatt_service->GetIdentifier());
     adapter_->NotifyGattServiceRemoved(scoped_service.get());
   }
   device_uuids_.ClearServiceUUIDs();
@@ -340,9 +340,8 @@
 device::BluetoothRemoteGattServiceMac*
 BluetoothLowEnergyDeviceMac::GetBluetoothRemoteGattService(
     CBService* cb_service) const {
-  for (GattServiceMap::const_iterator it = gatt_services_.begin();
-       it != gatt_services_.end(); ++it) {
-    device::BluetoothRemoteGattService* gatt_service = it->second;
+  for (auto it = gatt_services_.begin(); it != gatt_services_.end(); ++it) {
+    device::BluetoothRemoteGattService* gatt_service = it->second.get();
     device::BluetoothRemoteGattServiceMac* gatt_service_mac =
         static_cast<BluetoothRemoteGattServiceMac*>(gatt_service);
     if (gatt_service_mac->GetService() == cb_service)
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
index d40c006..5bcd437d 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
@@ -113,7 +113,7 @@
   EnsureDescriptorsCreated();
   std::vector<BluetoothRemoteGattDescriptor*> descriptors;
   for (const auto& map_iter : descriptors_)
-    descriptors.push_back(map_iter.second);
+    descriptors.push_back(map_iter.second.get());
   return descriptors;
 }
 
@@ -124,7 +124,7 @@
   const auto& iter = descriptors_.find(identifier);
   if (iter == descriptors_.end())
     return nullptr;
-  return iter->second;
+  return iter->second.get();
 }
 
 void BluetoothRemoteGattCharacteristicAndroid::ReadRemoteCharacteristic(
@@ -241,12 +241,11 @@
   std::string instanceIdString =
       base::android::ConvertJavaStringToUTF8(env, instanceId);
 
-  DCHECK(!descriptors_.contains(instanceIdString));
+  DCHECK(descriptors_.find(instanceIdString) == descriptors_.end());
 
-  descriptors_.set(instanceIdString,
-                   BluetoothRemoteGattDescriptorAndroid::Create(
-                       instanceIdString, bluetooth_gatt_descriptor_wrapper,
-                       chrome_bluetooth_device));
+  descriptors_[instanceIdString] = BluetoothRemoteGattDescriptorAndroid::Create(
+      instanceIdString, bluetooth_gatt_descriptor_wrapper,
+      chrome_bluetooth_device);
 }
 
 void BluetoothRemoteGattCharacteristicAndroid::SubscribeToNotifications(
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
index 5d7a4cf..06967e96 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
@@ -8,10 +8,10 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 
 #include "base/android/jni_android.h"
 #include "base/android/scoped_java_ref.h"
-#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/macros.h"
 #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
@@ -142,8 +142,8 @@
   std::vector<uint8_t> value_;
 
   // Map of descriptors, keyed by descriptor identifier.
-  base::ScopedPtrHashMap<std::string,
-                         std::unique_ptr<BluetoothRemoteGattDescriptorAndroid>>
+  std::unordered_map<std::string,
+                     std::unique_ptr<BluetoothRemoteGattDescriptorAndroid>>
       descriptors_;
 
   DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicAndroid);
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_android.cc b/device/bluetooth/bluetooth_remote_gatt_service_android.cc
index 25087b7..c7b20ad 100644
--- a/device/bluetooth/bluetooth_remote_gatt_service_android.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_service_android.cc
@@ -135,7 +135,7 @@
   EnsureCharacteristicsCreated();
   std::vector<device::BluetoothRemoteGattCharacteristic*> characteristics;
   for (const auto& map_iter : characteristics_)
-    characteristics.push_back(map_iter.second);
+    characteristics.push_back(map_iter.second.get());
   return characteristics;
 }
 
@@ -152,7 +152,7 @@
   const auto& iter = characteristics_.find(identifier);
   if (iter == characteristics_.end())
     return nullptr;
-  return iter->second;
+  return iter->second.get();
 }
 
 void BluetoothRemoteGattServiceAndroid::CreateGattRemoteCharacteristic(
@@ -166,13 +166,12 @@
   std::string instance_id_string =
       base::android::ConvertJavaStringToUTF8(env, instance_id);
 
-  DCHECK(!characteristics_.contains(instance_id_string));
+  DCHECK(characteristics_.find(instance_id_string) == characteristics_.end());
 
-  characteristics_.set(
-      instance_id_string,
+  characteristics_[instance_id_string] =
       BluetoothRemoteGattCharacteristicAndroid::Create(
           adapter_, this, instance_id_string,
-          bluetooth_gatt_characteristic_wrapper, chrome_bluetooth_device));
+          bluetooth_gatt_characteristic_wrapper, chrome_bluetooth_device);
 }
 
 BluetoothRemoteGattServiceAndroid::BluetoothRemoteGattServiceAndroid(
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_android.h b/device/bluetooth/bluetooth_remote_gatt_service_android.h
index ef3be1bc..3882702 100644
--- a/device/bluetooth/bluetooth_remote_gatt_service_android.h
+++ b/device/bluetooth/bluetooth_remote_gatt_service_android.h
@@ -8,11 +8,11 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/android/jni_android.h"
 #include "base/android/scoped_java_ref.h"
-#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/macros.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "device/bluetooth/bluetooth_uuid.h"
@@ -109,9 +109,8 @@
   std::string instance_id_;
 
   // Map of characteristics, keyed by characteristic identifier.
-  base::ScopedPtrHashMap<
-      std::string,
-      std::unique_ptr<BluetoothRemoteGattCharacteristicAndroid>>
+  std::unordered_map<std::string,
+                     std::unique_ptr<BluetoothRemoteGattCharacteristicAndroid>>
       characteristics_;
 
   DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceAndroid);
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
index e1ed672..f2a6884 100644
--- a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
@@ -15,6 +15,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
@@ -509,10 +510,9 @@
     BluetoothDevice::PairingDelegate* pairing_delegate) {
   // Check if any device is using the pairing delegate.
   // If so, clear the pairing context which will make any responses no-ops.
-  for (DevicesMap::const_iterator iter = devices_.begin();
-       iter != devices_.end(); ++iter) {
+  for (auto iter = devices_.begin(); iter != devices_.end(); ++iter) {
     BluetoothDeviceBlueZ* device_bluez =
-        static_cast<BluetoothDeviceBlueZ*>(iter->second);
+        static_cast<BluetoothDeviceBlueZ*>(iter->second.get());
 
     BluetoothPairingBlueZ* pairing = device_bluez->GetPairing();
     if (pairing && pairing->GetPairingDelegate() == pairing_delegate)
@@ -566,21 +566,19 @@
       this, object_path, ui_task_runner_, socket_thread_);
   DCHECK(devices_.find(device_bluez->GetAddress()) == devices_.end());
 
-  devices_.set(device_bluez->GetAddress(),
-               std::unique_ptr<BluetoothDevice>(device_bluez));
+  devices_[device_bluez->GetAddress()] = base::WrapUnique(device_bluez);
 
   for (auto& observer : observers_)
     observer.DeviceAdded(this, device_bluez);
 }
 
 void BluetoothAdapterBlueZ::DeviceRemoved(const dbus::ObjectPath& object_path) {
-  for (DevicesMap::const_iterator iter = devices_.begin();
-       iter != devices_.end(); ++iter) {
+  for (auto iter = devices_.begin(); iter != devices_.end(); ++iter) {
     BluetoothDeviceBlueZ* device_bluez =
-        static_cast<BluetoothDeviceBlueZ*>(iter->second);
+        static_cast<BluetoothDeviceBlueZ*>(iter->second.get());
     if (device_bluez->object_path() == object_path) {
-      std::unique_ptr<BluetoothDevice> scoped_device =
-          devices_.take_and_erase(iter->first);
+      std::unique_ptr<BluetoothDevice> scoped_device = std::move(iter->second);
+      devices_.erase(iter);
 
       for (auto& observer : observers_)
         observer.DeviceRemoved(this, device_bluez);
@@ -601,19 +599,17 @@
           object_path);
 
   if (property_name == properties->address.name()) {
-    for (DevicesMap::iterator iter = devices_.begin(); iter != devices_.end();
-         ++iter) {
+    for (auto iter = devices_.begin(); iter != devices_.end(); ++iter) {
       if (iter->second->GetAddress() == device_bluez->GetAddress()) {
         std::string old_address = iter->first;
         VLOG(1) << "Device changed address, old: " << old_address
                 << " new: " << device_bluez->GetAddress();
         std::unique_ptr<BluetoothDevice> scoped_device =
-            devices_.take_and_erase(iter);
-        ignore_result(scoped_device.release());
+            std::move(iter->second);
+        devices_.erase(iter);
 
         DCHECK(devices_.find(device_bluez->GetAddress()) == devices_.end());
-        devices_.set(device_bluez->GetAddress(),
-                     std::unique_ptr<BluetoothDevice>(device_bluez));
+        devices_[device_bluez->GetAddress()] = std::move(scoped_device);
         NotifyDeviceAddressChanged(device_bluez, old_address);
         break;
       }
@@ -670,8 +666,7 @@
 
     int count = 0;
 
-    for (DevicesMap::const_iterator iter = devices_.begin();
-         iter != devices_.end(); ++iter) {
+    for (auto iter = devices_.begin(); iter != devices_.end(); ++iter) {
       if (iter->second->IsPaired() && iter->second->IsConnected())
         ++count;
     }
@@ -903,10 +898,9 @@
   if (!IsPresent())
     return nullptr;
 
-  for (DevicesMap::const_iterator iter = devices_.begin();
-       iter != devices_.end(); ++iter) {
+  for (auto iter = devices_.begin(); iter != devices_.end(); ++iter) {
     BluetoothDeviceBlueZ* device_bluez =
-        static_cast<BluetoothDeviceBlueZ*>(iter->second);
+        static_cast<BluetoothDeviceBlueZ*>(iter->second.get());
     if (device_bluez->object_path() == object_path)
       return device_bluez;
   }
@@ -1041,7 +1035,7 @@
 
   for (auto& iter : devices_swapped) {
     for (auto& observer : observers_)
-      observer.DeviceRemoved(this, iter.second);
+      observer.DeviceRemoved(this, iter.second.get());
   }
 
   PresentChanged(false);
diff --git a/device/bluetooth/bluez/bluetooth_device_bluez.cc b/device/bluetooth/bluez/bluetooth_device_bluez.cc
index 273169fb..2a91a49f 100644
--- a/device/bluetooth/bluez/bluetooth_device_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_device_bluez.cc
@@ -11,6 +11,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -180,7 +181,7 @@
   for (const auto& iter : gatt_services_swapped) {
     DCHECK(adapter());
     adapter()->NotifyGattServiceRemoved(
-        static_cast<BluetoothRemoteGattServiceBlueZ*>(iter.second));
+        static_cast<BluetoothRemoteGattServiceBlueZ*>(iter.second.get()));
   }
 }
 
@@ -678,8 +679,7 @@
   BluetoothRemoteGattServiceBlueZ* service =
       new BluetoothRemoteGattServiceBlueZ(adapter(), this, object_path);
 
-  gatt_services_.set(service->GetIdentifier(),
-                     std::unique_ptr<BluetoothRemoteGattService>(service));
+  gatt_services_[service->GetIdentifier()] = base::WrapUnique(service);
   DCHECK(service->object_path() == object_path);
   DCHECK(service->GetUUID().IsValid());
 
@@ -689,15 +689,14 @@
 
 void BluetoothDeviceBlueZ::GattServiceRemoved(
     const dbus::ObjectPath& object_path) {
-  GattServiceMap::const_iterator iter =
-      gatt_services_.find(object_path.value());
+  auto iter = gatt_services_.find(object_path.value());
   if (iter == gatt_services_.end()) {
     VLOG(3) << "Unknown GATT service removed: " << object_path.value();
     return;
   }
 
   BluetoothRemoteGattServiceBlueZ* service =
-      static_cast<BluetoothRemoteGattServiceBlueZ*>(iter->second);
+      static_cast<BluetoothRemoteGattServiceBlueZ*>(iter->second.get());
 
   VLOG(1) << "Removing remote GATT service with UUID: '"
           << service->GetUUID().canonical_value()
@@ -705,7 +704,8 @@
 
   DCHECK(service->object_path() == object_path);
   std::unique_ptr<BluetoothRemoteGattService> scoped_service =
-      gatt_services_.take_and_erase(iter->first);
+      std::move(gatt_services_[object_path.value()]);
+  gatt_services_.erase(iter);
 
   DCHECK(adapter());
   discovery_complete_notified_.erase(service);
diff --git a/device/bluetooth/bluez/bluetooth_device_bluez.h b/device/bluetooth/bluez/bluetooth_device_bluez.h
index d0cc1fac..89e7ce77 100644
--- a/device/bluetooth/bluez/bluetooth_device_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_device_bluez.h
@@ -49,6 +49,8 @@
   using GetServiceRecordsErrorCallback =
       base::Callback<void(BluetoothServiceRecordBlueZ::ErrorCode)>;
 
+  ~BluetoothDeviceBlueZ() override;
+
   // BluetoothDevice override
   uint32_t GetBluetoothClass() const override;
   device::BluetoothTransport GetType() const override;
@@ -164,7 +166,6 @@
       const dbus::ObjectPath& object_path,
       scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
       scoped_refptr<device::BluetoothSocketThread> socket_thread);
-  ~BluetoothDeviceBlueZ() override;
 
   // bluez::BluetoothGattServiceClient::Observer overrides
   void GattServiceAdded(const dbus::ObjectPath& object_path) override;
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h b/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h
index c08375b..90acbe4 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h
@@ -42,6 +42,8 @@
       public BluetoothGattCharacteristicClient::Observer,
       public device::BluetoothRemoteGattService {
  public:
+  ~BluetoothRemoteGattServiceBlueZ() override;
+
   // device::BluetoothRemoteGattService overrides.
   device::BluetoothUUID GetUUID() const override;
   device::BluetoothDevice* GetDevice() const override;
@@ -83,7 +85,6 @@
   BluetoothRemoteGattServiceBlueZ(BluetoothAdapterBlueZ* adapter,
                                   BluetoothDeviceBlueZ* device,
                                   const dbus::ObjectPath& object_path);
-  ~BluetoothRemoteGattServiceBlueZ() override;
 
   // bluez::BluetoothGattServiceClient::Observer override.
   void GattServicePropertyChanged(const dbus::ObjectPath& object_path,
diff --git a/extensions/common/error_utils.cc b/extensions/common/error_utils.cc
index 14ca2a9..f8f04b7 100644
--- a/extensions/common/error_utils.cc
+++ b/extensions/common/error_utils.cc
@@ -9,48 +9,48 @@
 
 namespace extensions {
 
-std::string ErrorUtils::FormatErrorMessage(const std::string& format,
-                                           const std::string& s1) {
-  std::string ret_val = format;
+std::string ErrorUtils::FormatErrorMessage(base::StringPiece format,
+                                           base::StringPiece s1) {
+  std::string ret_val = format.as_string();
   base::ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
   return ret_val;
 }
 
-std::string ErrorUtils::FormatErrorMessage(const std::string& format,
-                                           const std::string& s1,
-                                           const std::string& s2) {
-  std::string ret_val = format;
+std::string ErrorUtils::FormatErrorMessage(base::StringPiece format,
+                                           base::StringPiece s1,
+                                           base::StringPiece s2) {
+  std::string ret_val = format.as_string();
   base::ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
   base::ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
   return ret_val;
 }
 
-std::string ErrorUtils::FormatErrorMessage(const std::string& format,
-                                           const std::string& s1,
-                                           const std::string& s2,
-                                           const std::string& s3) {
-  std::string ret_val = format;
+std::string ErrorUtils::FormatErrorMessage(base::StringPiece format,
+                                           base::StringPiece s1,
+                                           base::StringPiece s2,
+                                           base::StringPiece s3) {
+  std::string ret_val = format.as_string();
   base::ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
   base::ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
   base::ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s3);
   return ret_val;
 }
 
-base::string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format,
-                                                   const std::string& s1) {
+base::string16 ErrorUtils::FormatErrorMessageUTF16(base::StringPiece format,
+                                                   base::StringPiece s1) {
   return base::UTF8ToUTF16(FormatErrorMessage(format, s1));
 }
 
-base::string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format,
-                                                   const std::string& s1,
-                                                   const std::string& s2) {
+base::string16 ErrorUtils::FormatErrorMessageUTF16(base::StringPiece format,
+                                                   base::StringPiece s1,
+                                                   base::StringPiece s2) {
   return base::UTF8ToUTF16(FormatErrorMessage(format, s1, s2));
 }
 
-base::string16 ErrorUtils::FormatErrorMessageUTF16(const std::string& format,
-                                                   const std::string& s1,
-                                                   const std::string& s2,
-                                                   const std::string& s3) {
+base::string16 ErrorUtils::FormatErrorMessageUTF16(base::StringPiece format,
+                                                   base::StringPiece s1,
+                                                   base::StringPiece s2,
+                                                   base::StringPiece s3) {
   return base::UTF8ToUTF16(FormatErrorMessage(format, s1, s2, s3));
 }
 
diff --git a/extensions/common/error_utils.h b/extensions/common/error_utils.h
index fedbd28..5f86ee0b 100644
--- a/extensions/common/error_utils.h
+++ b/extensions/common/error_utils.h
@@ -8,35 +8,36 @@
 #include <string>
 
 #include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
 
 namespace extensions {
 
 class ErrorUtils {
  public:
   // Creates an error messages from a pattern.
-  static std::string FormatErrorMessage(const std::string& format,
-                                        const std::string& s1);
+  static std::string FormatErrorMessage(base::StringPiece format,
+                                        base::StringPiece s1);
 
-  static std::string FormatErrorMessage(const std::string& format,
-                                        const std::string& s1,
-                                        const std::string& s2);
+  static std::string FormatErrorMessage(base::StringPiece format,
+                                        base::StringPiece s1,
+                                        base::StringPiece s2);
 
-  static std::string FormatErrorMessage(const std::string& format,
-                                        const std::string& s1,
-                                        const std::string& s2,
-                                        const std::string& s3);
+  static std::string FormatErrorMessage(base::StringPiece format,
+                                        base::StringPiece s1,
+                                        base::StringPiece s2,
+                                        base::StringPiece s3);
 
-  static base::string16 FormatErrorMessageUTF16(const std::string& format,
-                                                const std::string& s1);
+  static base::string16 FormatErrorMessageUTF16(base::StringPiece format,
+                                                base::StringPiece s1);
 
-  static base::string16 FormatErrorMessageUTF16(const std::string& format,
-                                                const std::string& s1,
-                                                const std::string& s2);
+  static base::string16 FormatErrorMessageUTF16(base::StringPiece format,
+                                                base::StringPiece s1,
+                                                base::StringPiece s2);
 
-  static base::string16 FormatErrorMessageUTF16(const std::string& format,
-                                                const std::string& s1,
-                                                const std::string& s2,
-                                                const std::string& s3);
+  static base::string16 FormatErrorMessageUTF16(base::StringPiece format,
+                                                base::StringPiece s1,
+                                                base::StringPiece s2,
+                                                base::StringPiece s3);
 };
 
 }  // namespace extensions
diff --git a/extensions/renderer/api_binding_test_util.cc b/extensions/renderer/api_binding_test_util.cc
index d1b0cc8..57fa7af 100644
--- a/extensions/renderer/api_binding_test_util.cc
+++ b/extensions/renderer/api_binding_test_util.cc
@@ -175,4 +175,21 @@
   return V8ToBaseValue(GetPropertyFromObject(object, context, key), context);
 }
 
+std::string GetStringPropertyFromObject(v8::Local<v8::Object> object,
+                                        v8::Local<v8::Context> context,
+                                        base::StringPiece key) {
+  v8::Local<v8::Value> v8_val = GetPropertyFromObject(object, context, key);
+  if (v8_val.IsEmpty())
+    return "empty";
+  if (v8_val->IsNull())
+    return "null";
+  if (v8_val->IsUndefined())
+    return "undefined";
+  if (v8_val->IsFunction())
+    return "function";
+  std::unique_ptr<base::Value> json_prop = V8ToBaseValue(v8_val, context);
+  DCHECK(json_prop) << key;
+  return ValueToString(*json_prop);
+}
+
 }  // namespace extensions
diff --git a/extensions/renderer/api_binding_test_util.h b/extensions/renderer/api_binding_test_util.h
index aa98697..3cdfdfa 100644
--- a/extensions/renderer/api_binding_test_util.h
+++ b/extensions/renderer/api_binding_test_util.h
@@ -106,6 +106,12 @@
     v8::Local<v8::Context> context,
     base::StringPiece key);
 
+// As above, but returns a JSON-serialized version of the value, or
+// "undefined", "null", "function", or "empty".
+std::string GetStringPropertyFromObject(v8::Local<v8::Object> object,
+                                        v8::Local<v8::Context> context,
+                                        base::StringPiece key);
+
 }  // extensions
 
 #endif  // EXTENSIONS_RENDERER_API_BINDING_TEST_UTIL_H_
diff --git a/extensions/renderer/api_bindings_system_unittest.cc b/extensions/renderer/api_bindings_system_unittest.cc
index 65860387..214f2f6 100644
--- a/extensions/renderer/api_bindings_system_unittest.cc
+++ b/extensions/renderer/api_bindings_system_unittest.cc
@@ -232,10 +232,9 @@
     bindings_system()->CompleteRequest(last_request()->request_id,
                                        *expected_args);
 
-    std::unique_ptr<base::Value> result = GetBaseValuePropertyFromObject(
-        context->Global(), context, "callbackArguments");
-    ASSERT_TRUE(result);
-    EXPECT_EQ(ReplaceSingleQuotes(kResponseArgsJson), ValueToString(*result));
+    EXPECT_EQ(ReplaceSingleQuotes(kResponseArgsJson),
+              GetStringPropertyFromObject(context->Global(), context,
+                                          "callbackArguments"));
     reset_last_request();
   }
 
@@ -255,10 +254,8 @@
     bindings_system()->CompleteRequest(last_request()->request_id,
                                        base::ListValue());
 
-    std::unique_ptr<base::Value> result = GetBaseValuePropertyFromObject(
-        context->Global(), context, "callbackArguments");
-    ASSERT_TRUE(result);
-    EXPECT_EQ("[]", ValueToString(*result));
+    EXPECT_EQ("[]", GetStringPropertyFromObject(context->Global(), context,
+                                                "callbackArguments"));
     reset_last_request();
   }
 
@@ -276,10 +273,9 @@
     bindings_system()->FireEventInContext("alpha.alphaEvent", context,
                                           *expected_args);
 
-    std::unique_ptr<base::Value> result = GetBaseValuePropertyFromObject(
-        context->Global(), context, "eventArguments");
-    ASSERT_TRUE(result);
-    EXPECT_EQ(ReplaceSingleQuotes(kResponseArgsJson), ValueToString(*result));
+    EXPECT_EQ(ReplaceSingleQuotes(kResponseArgsJson),
+              GetStringPropertyFromObject(context->Global(), context,
+                                          "eventArguments"));
   }
 
   {
@@ -339,10 +335,9 @@
     CallFunctionOnObject(context, alpha_api, kTestCall);
     EXPECT_TRUE(did_call);
 
-    std::unique_ptr<base::Value> result = GetBaseValuePropertyFromObject(
-        context->Global(), context, "callbackArguments");
-    ASSERT_TRUE(result);
-    EXPECT_EQ("[\"bar\"]", ValueToString(*result));
+    EXPECT_EQ("[\"bar\"]",
+              GetStringPropertyFromObject(context->Global(), context,
+                                          "callbackArguments"));
   }
 }
 
@@ -485,16 +480,13 @@
         "  this.idleState = state;\n"
         "});\n";
     ExecuteScript(context, kTestCall);
-    v8::Local<v8::Value> v8_result =
-        GetPropertyFromObject(context->Global(), context, "idleState");
-    EXPECT_TRUE(v8_result->IsUndefined());
+    EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(),
+                                                       context, "idleState"));
     bindings_system()->FireEventInContext("idle.onStateChanged", context,
                                           *ListValueFromString("['active']"));
 
-    std::unique_ptr<base::Value> result =
-        GetBaseValuePropertyFromObject(context->Global(), context, "idleState");
-    ASSERT_TRUE(result);
-    EXPECT_EQ("\"active\"", ValueToString(*result));
+    EXPECT_EQ("\"active\"", GetStringPropertyFromObject(context->Global(),
+                                                        context, "idleState"));
   }
 }
 
diff --git a/extensions/renderer/api_event_handler_unittest.cc b/extensions/renderer/api_event_handler_unittest.cc
index 10b0ccc..b4efa1a 100644
--- a/extensions/renderer/api_event_handler_unittest.cc
+++ b/extensions/renderer/api_event_handler_unittest.cc
@@ -265,10 +265,9 @@
   ASSERT_TRUE(event_args);
   handler.FireEventInContext(kEventName, context, *event_args);
 
-  std::unique_ptr<base::Value> result =
-      GetBaseValuePropertyFromObject(context->Global(), context, "eventArgs");
-  ASSERT_TRUE(result);
-  EXPECT_EQ(ReplaceSingleQuotes(kArguments), ValueToString(*result));
+  EXPECT_EQ(
+      ReplaceSingleQuotes(kArguments),
+      GetStringPropertyFromObject(context->Global(), context, "eventArgs"));
 }
 
 // Test dispatching events to multiple contexts.
@@ -328,16 +327,13 @@
 
   handler.FireEventInContext(kEventName, context_a, *arguments_a);
   {
-    std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject(
-        context_a->Global(), context_a, "eventArgs");
-    ASSERT_TRUE(result_a);
-    EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a));
+    EXPECT_EQ("\"result_a:alpha\"",
+              GetStringPropertyFromObject(context_a->Global(), context_a,
+                                          "eventArgs"));
   }
   {
-    v8::Local<v8::Value> result_b =
-        GetPropertyFromObject(context_b->Global(), context_b, "eventArgs");
-    ASSERT_FALSE(result_b.IsEmpty());
-    EXPECT_TRUE(result_b->IsUndefined());
+    EXPECT_EQ("undefined", GetStringPropertyFromObject(context_b->Global(),
+                                                       context_b, "eventArgs"));
   }
 
   // Dispatch the event in context_b - the listener in context_a should not be
@@ -347,16 +343,14 @@
   ASSERT_TRUE(arguments_b);
   handler.FireEventInContext(kEventName, context_b, *arguments_b);
   {
-    std::unique_ptr<base::Value> result_a = GetBaseValuePropertyFromObject(
-        context_a->Global(), context_a, "eventArgs");
-    ASSERT_TRUE(result_a);
-    EXPECT_EQ("\"result_a:alpha\"", ValueToString(*result_a));
+    EXPECT_EQ("\"result_a:alpha\"",
+              GetStringPropertyFromObject(context_a->Global(), context_a,
+                                          "eventArgs"));
   }
   {
-    std::unique_ptr<base::Value> result_b = GetBaseValuePropertyFromObject(
-        context_b->Global(), context_b, "eventArgs");
-    ASSERT_TRUE(result_b);
-    EXPECT_EQ("\"result_b:beta\"", ValueToString(*result_b));
+    EXPECT_EQ("\"result_b:beta\"",
+              GetStringPropertyFromObject(context_b->Global(), context_b,
+                                          "eventArgs"));
   }
 }
 
diff --git a/extensions/renderer/api_request_handler_unittest.cc b/extensions/renderer/api_request_handler_unittest.cc
index 5333f7e..1aa6fc9 100644
--- a/extensions/renderer/api_request_handler_unittest.cc
+++ b/extensions/renderer/api_request_handler_unittest.cc
@@ -78,10 +78,8 @@
   request_handler.CompleteRequest(request_id, *response_arguments);
 
   EXPECT_TRUE(did_run_js());
-  std::unique_ptr<base::Value> result_value =
-      GetBaseValuePropertyFromObject(context->Global(), context, "result");
-  ASSERT_TRUE(result_value);
-  EXPECT_EQ(ReplaceSingleQuotes(kArguments), ValueToString(*result_value));
+  EXPECT_EQ(ReplaceSingleQuotes(kArguments),
+            GetStringPropertyFromObject(context->Global(), context, "result"));
 
   EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
 }
@@ -154,11 +152,9 @@
   EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
               testing::UnorderedElementsAre(request_b));
 
-  std::unique_ptr<base::Value> result_a =
-      GetBaseValuePropertyFromObject(context_a->Global(), context_a, "result");
-  ASSERT_TRUE(result_a);
-  EXPECT_EQ(ReplaceSingleQuotes("'response_a:alpha'"),
-            ValueToString(*result_a));
+  EXPECT_EQ(
+      ReplaceSingleQuotes("'response_a:alpha'"),
+      GetStringPropertyFromObject(context_a->Global(), context_a, "result"));
 
   std::unique_ptr<base::ListValue> response_b =
       ListValueFromString("['response_b:']");
@@ -167,10 +163,9 @@
   request_handler.CompleteRequest(request_b, *response_b);
   EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
 
-  std::unique_ptr<base::Value> result_b =
-      GetBaseValuePropertyFromObject(context_b->Global(), context_b, "result");
-  ASSERT_TRUE(result_b);
-  EXPECT_EQ(ReplaceSingleQuotes("'response_b:beta'"), ValueToString(*result_b));
+  EXPECT_EQ(
+      ReplaceSingleQuotes("'response_b:beta'"),
+      GetStringPropertyFromObject(context_b->Global(), context_b, "result"));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/native_extension_bindings_system_unittest.cc b/extensions/renderer/native_extension_bindings_system_unittest.cc
index 62d4d99d..5045c87 100644
--- a/extensions/renderer/native_extension_bindings_system_unittest.cc
+++ b/extensions/renderer/native_extension_bindings_system_unittest.cc
@@ -318,37 +318,29 @@
     RunFunctionOnGlobal(call_idle_query_state, context, 0, nullptr);
   }
 
-  auto get_property_as_string = [&context](v8::Local<v8::Object> object,
-                                           base::StringPiece property_name) {
-    std::unique_ptr<base::Value> property =
-        GetBaseValuePropertyFromObject(object, context, property_name);
-    if (!property)
-      return std::string();
-    return ValueToString(*property);
-  };
-
   // To start, check that the properties we set when running the hooks are
   // correct. We do this after calling the function because the API objects (and
   // thus the hooks) are set up lazily.
   v8::Local<v8::Object> global = context->Global();
   EXPECT_EQ(base::StringPrintf("\"%s\"", extension->id().c_str()),
-            get_property_as_string(global, "hookedExtensionId"));
+            GetStringPropertyFromObject(global, context, "hookedExtensionId"));
   EXPECT_EQ("\"BLESSED_EXTENSION\"",
-            get_property_as_string(global, "hookedContextType"));
+            GetStringPropertyFromObject(global, context, "hookedContextType"));
   v8::Local<v8::Value> idle_api =
       V8ValueFromScriptSource(context, "chrome.idle");
   ASSERT_FALSE(idle_api.IsEmpty());
   ASSERT_TRUE(idle_api->IsObject());
   EXPECT_EQ("\"someProperty\"",
-            get_property_as_string(idle_api.As<v8::Object>(),
-                                   "hookedApiProperty"));
+            GetStringPropertyFromObject(idle_api.As<v8::Object>(), context,
+                                        "hookedApiProperty"));
 
   // Next, we need to check two pieces: first, that the custom handler was
   // called with the proper arguments....
-  EXPECT_EQ("30", get_property_as_string(global, "timeArg"));
+  EXPECT_EQ("30", GetStringPropertyFromObject(global, context, "timeArg"));
 
   // ...and second, that the callback was called with the proper result.
-  EXPECT_EQ("\"active\"", get_property_as_string(global, "responseState"));
+  EXPECT_EQ("\"active\"",
+            GetStringPropertyFromObject(global, context, "responseState"));
 }
 
 }  // namespace extensions
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index fc3c5cb..1b4dfa6f 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -180,11 +180,9 @@
       "lib/wtf_clone_equals_util.h",
       "lib/wtf_hash_util.h",
       "lib/wtf_serialization.h",
-      "map_traits_wtf.h",
       "map_traits_wtf_hash_map.h",
       "string_traits_wtf.h",
       "wtf_array.h",
-      "wtf_map.h",
     ]
 
     public_deps = [
diff --git a/mojo/public/cpp/bindings/lib/wtf_serialization.h b/mojo/public/cpp/bindings/lib/wtf_serialization.h
index 132e19c..40766bb 100644
--- a/mojo/public/cpp/bindings/lib/wtf_serialization.h
+++ b/mojo/public/cpp/bindings/lib/wtf_serialization.h
@@ -7,7 +7,6 @@
 
 #include "mojo/public/cpp/bindings/array_traits_wtf.h"
 #include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
-#include "mojo/public/cpp/bindings/map_traits_wtf.h"
 #include "mojo/public/cpp/bindings/map_traits_wtf_hash_map.h"
 #include "mojo/public/cpp/bindings/string_traits_wtf.h"
 
diff --git a/mojo/public/cpp/bindings/map_traits_wtf.h b/mojo/public/cpp/bindings/map_traits_wtf.h
deleted file mode 100644
index 805e4c98..0000000
--- a/mojo/public/cpp/bindings/map_traits_wtf.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
-
-#include "base/logging.h"
-#include "mojo/public/cpp/bindings/map_traits.h"
-#include "mojo/public/cpp/bindings/wtf_map.h"
-
-namespace mojo {
-
-template <typename K, typename V>
-struct MapTraits<WTFMap<K, V>> {
-  using Key = K;
-  using Value = V;
-  using Iterator = typename WTFMap<K, V>::Iterator;
-  using ConstIterator = typename WTFMap<K, V>::ConstIterator;
-
-  static bool IsNull(const WTFMap<K, V>& input) { return input.is_null(); }
-  static void SetToNull(WTFMap<K, V>* output) { *output = nullptr; }
-
-  static size_t GetSize(const WTFMap<K, V>& input) { return input.size(); }
-
-  static ConstIterator GetBegin(const WTFMap<K, V>& input) {
-    return input.begin();
-  }
-  static Iterator GetBegin(WTFMap<K, V>& input) { return input.begin(); }
-
-  static void AdvanceIterator(ConstIterator& iterator) { ++iterator; }
-  static void AdvanceIterator(Iterator& iterator) { ++iterator; }
-
-  static const K& GetKey(Iterator& iterator) { return iterator->key; }
-  static const K& GetKey(ConstIterator& iterator) { return iterator->key; }
-
-  static V& GetValue(Iterator& iterator) { return iterator->value; }
-  static const V& GetValue(ConstIterator& iterator) { return iterator->value; }
-
-  static bool Insert(WTFMap<K, V>& input, const K& key, V&& value) {
-    if (!WTFMap<K, V>::IsValidKey(key)) {
-      LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
-      return false;
-    }
-    input.insert(key, std::forward<V>(value));
-    return true;
-  }
-  static bool Insert(WTFMap<K, V>& input, const K& key, const V& value) {
-    if (!WTFMap<K, V>::IsValidKey(key)) {
-      LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
-      return false;
-    }
-    input.insert(key, value);
-    return true;
-  }
-
-  static void SetToEmpty(WTFMap<K, V>* output) { output->SetToEmpty(); }
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
diff --git a/mojo/public/cpp/bindings/struct_traits.h b/mojo/public/cpp/bindings/struct_traits.h
index a1379fe2..d570eac 100644
--- a/mojo/public/cpp/bindings/struct_traits.h
+++ b/mojo/public/cpp/bindings/struct_traits.h
@@ -39,7 +39,7 @@
 //        - map:
 //          Value or reference of any type that has a MapTraits defined.
 //          Supported by default: std::map, std::unordered_map, mojo::Map,
-//          WTF::HashMap (in blink), mojo::WTFMap (in blink).
+//          WTF::HashMap (in blink).
 //
 //        - struct:
 //          Value or reference of any type that has a StructTraits defined.
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index 6b7472d8..2e61173 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -83,11 +83,9 @@
       "array_common_test.h",
       "container_test_util.cc",
       "container_test_util.h",
-      "map_common_test.h",
       "variant_test_util.h",
       "wtf_array_unittest.cc",
       "wtf_hash_unittest.cc",
-      "wtf_map_unittest.cc",
       "wtf_types_unittest.cc",
     ]
 
diff --git a/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc
deleted file mode 100644
index 562e1e2..0000000
--- a/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/bindings/wtf_map.h"
-
-#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "mojo/public/cpp/bindings/tests/map_common_test.h"
-#include "mojo/public/cpp/bindings/tests/rect_blink.h"
-#include "mojo/public/cpp/bindings/wtf_array.h"
-#include "mojo/public/interfaces/bindings/tests/rect.mojom-blink.h"
-#include "mojo/public/interfaces/bindings/tests/test_structs.mojom-blink.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/Source/wtf/text/WTFString.h"
-
-namespace mojo {
-namespace test {
-
-namespace {
-
-using WTFMapTest = testing::Test;
-
-MAP_COMMON_TEST(WTFMap, NullAndEmpty)
-MAP_COMMON_TEST(WTFMap, InsertWorks)
-MAP_COMMON_TEST(WTFMap, TestIndexOperator)
-MAP_COMMON_TEST(WTFMap, TestIndexOperatorAsRValue)
-MAP_COMMON_TEST(WTFMap, TestIndexOperatorMoveOnly)
-MAP_COMMON_TEST(WTFMap, MapArrayClone)
-MAP_COMMON_TEST(WTFMap, ArrayOfMap)
-
-TEST_F(WTFMapTest, MoveFromAndToWTFHashMap_Copyable) {
-  WTF::HashMap<int32_t, CopyableType> map1;
-  map1.add(123, CopyableType());
-  map1.find(123)->value.ResetCopied();
-  ASSERT_FALSE(map1.find(123)->value.copied());
-
-  WTFMap<int32_t, CopyableType> mojo_map(std::move(map1));
-  ASSERT_EQ(1u, mojo_map.size());
-  ASSERT_NE(mojo_map.end(), mojo_map.find(123));
-  ASSERT_FALSE(mojo_map[123].copied());
-
-  WTF::HashMap<int32_t, CopyableType> map2(mojo_map.PassStorage());
-  ASSERT_EQ(1u, map2.size());
-  ASSERT_NE(map2.end(), map2.find(123));
-  ASSERT_FALSE(map2.find(123)->value.copied());
-
-  ASSERT_EQ(0u, mojo_map.size());
-  ASSERT_TRUE(mojo_map.is_null());
-}
-
-TEST_F(WTFMapTest, MoveFromAndToWTFHashMap_MoveOnly) {
-  WTF::HashMap<int32_t, MoveOnlyType> map1;
-  map1.add(123, MoveOnlyType());
-
-  WTFMap<int32_t, MoveOnlyType> mojo_map(std::move(map1));
-  ASSERT_EQ(1u, mojo_map.size());
-  ASSERT_NE(mojo_map.end(), mojo_map.find(123));
-
-  WTF::HashMap<int32_t, MoveOnlyType> map2(mojo_map.PassStorage());
-  ASSERT_EQ(1u, map2.size());
-  ASSERT_NE(map2.end(), map2.find(123));
-
-  ASSERT_EQ(0u, mojo_map.size());
-  ASSERT_TRUE(mojo_map.is_null());
-}
-
-static blink::RectPtr MakeRect(int32_t x,
-                               int32_t y,
-                               int32_t width,
-                               int32_t height) {
-  blink::RectPtr rect_ptr = blink::Rect::New();
-  rect_ptr->x = x;
-  rect_ptr->y = y;
-  rect_ptr->width = width;
-  rect_ptr->height = height;
-  return rect_ptr;
-}
-
-TEST_F(WTFMapTest, StructKey) {
-  WTF::HashMap<blink::RectPtr, int32_t> map;
-  map.add(MakeRect(1, 2, 3, 4), 123);
-
-  blink::RectPtr key = MakeRect(1, 2, 3, 4);
-  ASSERT_NE(map.end(), map.find(key));
-  ASSERT_EQ(123, map.find(key)->value);
-
-  map.remove(key);
-  ASSERT_EQ(0u, map.size());
-}
-
-static blink::ContainsHashablePtr MakeContainsHashablePtr(RectBlink rect) {
-  blink::ContainsHashablePtr ptr = blink::ContainsHashable::New();
-  ptr->rect = rect;
-  return ptr;
-}
-
-TEST_F(WTFMapTest, TypemappedStructKey) {
-  WTF::HashMap<blink::ContainsHashablePtr, int32_t> map;
-  map.add(MakeContainsHashablePtr(RectBlink(1, 2, 3, 4)), 123);
-
-  blink::ContainsHashablePtr key =
-      MakeContainsHashablePtr(RectBlink(1, 2, 3, 4));
-  ASSERT_NE(map.end(), map.find(key));
-  ASSERT_EQ(123, map.find(key)->value);
-
-  map.remove(key);
-  ASSERT_EQ(0u, map.size());
-}
-
-}  // namespace
-}  // namespace test
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
index cc2963a..d18241e56 100644
--- a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
@@ -177,66 +177,6 @@
   EXPECT_TRUE(kUTF8HelloWorld == strs2[3]);
 }
 
-TEST_F(WTFTypesTest, Serialization_WTFMapToWTFMap) {
-  using WTFType = WTFMap<WTF::String, WTF::String>;
-  using MojomType = MapDataView<StringDataView, StringDataView>;
-
-  WTFType str_map = ConstructStringMap();
-  WTFType cloned_str_map = str_map.Clone();
-
-  mojo::internal::SerializationContext context;
-  size_t size =
-      mojo::internal::PrepareToSerialize<MojomType>(cloned_str_map, &context);
-
-  mojo::internal::FixedBufferForTesting buf(size);
-  typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
-  mojo::internal::ContainerValidateParams validate_params(
-      new mojo::internal::ContainerValidateParams(
-          0, false,
-          new mojo::internal::ContainerValidateParams(0, false, nullptr)),
-      new mojo::internal::ContainerValidateParams(
-          0, true,
-          new mojo::internal::ContainerValidateParams(0, false, nullptr)));
-  mojo::internal::Serialize<MojomType>(cloned_str_map, &buf, &data,
-                                       &validate_params, &context);
-
-  WTFType str_map2;
-  mojo::internal::Deserialize<MojomType>(data, &str_map2, &context);
-
-  EXPECT_TRUE(str_map.Equals(str_map2));
-}
-
-TEST_F(WTFTypesTest, Serialization_WTFMapToMojoMap) {
-  using WTFType = WTFMap<WTF::String, WTF::String>;
-  using MojomType = MapDataView<StringDataView, StringDataView>;
-
-  WTFType str_map = ConstructStringMap();
-
-  mojo::internal::SerializationContext context;
-  size_t size =
-      mojo::internal::PrepareToSerialize<MojomType>(str_map, &context);
-
-  mojo::internal::FixedBufferForTesting buf(size);
-  typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
-  mojo::internal::ContainerValidateParams validate_params(
-      new mojo::internal::ContainerValidateParams(
-          0, false,
-          new mojo::internal::ContainerValidateParams(0, false, nullptr)),
-      new mojo::internal::ContainerValidateParams(
-          0, true,
-          new mojo::internal::ContainerValidateParams(0, false, nullptr)));
-  mojo::internal::Serialize<MojomType>(str_map, &buf, &data, &validate_params,
-                                       &context);
-
-  Map<mojo::String, mojo::String> str_map2;
-  mojo::internal::Deserialize<MojomType>(data, &str_map2, &context);
-
-  ASSERT_EQ(3u, str_map2.size());
-  EXPECT_TRUE(str_map2["0"].is_null());
-  EXPECT_TRUE(kHelloWorld == str_map2["1"]);
-  EXPECT_TRUE(kUTF8HelloWorld == str_map2["2"]);
-}
-
 TEST_F(WTFTypesTest, Serialization_PublicAPI) {
   blink::TestWTFStructPtr input(blink::TestWTFStruct::New());
   input->str = kHelloWorld;
diff --git a/mojo/public/cpp/bindings/wtf_map.h b/mojo/public/cpp/bindings/wtf_map.h
deleted file mode 100644
index 0aba959..0000000
--- a/mojo/public/cpp/bindings/wtf_map.h
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
-
-#include <stddef.h>
-#include <utility>
-
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/lib/template_util.h"
-#include "mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h"
-#include "mojo/public/cpp/bindings/type_converter.h"
-#include "third_party/WebKit/Source/wtf/HashMap.h"
-#include "third_party/WebKit/Source/wtf/text/StringHash.h"
-
-namespace mojo {
-
-// Represents a map backed by WTF::HashMap. Comparing with WTF::HashMap,
-// mojo::WTFMap is move-only and can be null.
-//
-// It is easy to convert between WTF::HashMap<K, V> and mojo::WTFMap<K, V>:
-//   - constructor WTFMap(WTF::HashMap<K, V>&&) takes the contents of a
-//     WTF::HashMap<K, V>;
-//   - method PassStorage() passes the underlying WTF::HashMap.
-//
-// NOTE: WTF::HashMap disallows certain key values. For integer types, those are
-// 0 and -1 (max value instead of -1 for unsigned). For string, that is null.
-template <typename Key, typename Value>
-class WTFMap {
- public:
-  using Iterator = typename WTF::HashMap<Key, Value>::iterator;
-  using ConstIterator = typename WTF::HashMap<Key, Value>::const_iterator;
-
-  // Constructs an empty map.
-  WTFMap() : is_null_(false) {}
-  // Constructs a null map.
-  WTFMap(std::nullptr_t null_pointer) : is_null_(true) {}
-
-  ~WTFMap() {}
-
-  WTFMap(WTF::HashMap<Key, Value>&& other)
-      : map_(std::move(other)), is_null_(false) {}
-  WTFMap(WTFMap&& other) : is_null_(true) { Take(&other); }
-
-  WTFMap& operator=(WTF::HashMap<Key, Value>&& other) {
-    is_null_ = false;
-    map_ = std::move(other);
-    return *this;
-  }
-  WTFMap& operator=(WTFMap&& other) {
-    Take(&other);
-    return *this;
-  }
-
-  WTFMap& operator=(std::nullptr_t null_pointer) {
-    is_null_ = true;
-    map_.clear();
-    return *this;
-  }
-
-  static bool IsValidKey(const Key& key) {
-    return WTF::HashMap<Key, Value>::isValidKey(key);
-  }
-
-  // Copies the contents of some other type of map into a new WTFMap using a
-  // TypeConverter.
-  template <typename U>
-  static WTFMap From(const U& other) {
-    return TypeConverter<WTFMap, U>::Convert(other);
-  }
-
-  // Copies the contents of the WTFMap into some other type of map.
-  template <typename U>
-  U To() const {
-    return TypeConverter<U, WTFMap>::Convert(*this);
-  }
-
-  // Indicates whether the map is null (which is distinct from empty).
-  bool is_null() const { return is_null_; }
-
-  // Indicates whether the map is empty (which is distinct from null).
-  bool empty() const { return map_.isEmpty() && !is_null_; }
-
-  // Indicates the number of keys in the map, which will be zero if the map is
-  // null.
-  size_t size() const { return map_.size(); }
-
-  // Inserts a key-value pair into the map. Like WTF::HashMap::add(), this does
-  // not insert |value| if |key| is already a member of the map.
-  void insert(const Key& key, const Value& value) {
-    is_null_ = false;
-    map_.add(key, value);
-  }
-  void insert(const Key& key, Value&& value) {
-    is_null_ = false;
-    map_.add(key, std::move(value));
-  }
-
-  // Returns a reference to the value associated with the specified key,
-  // crashing the process if the key is not present in the map.
-  Value& at(const Key& key) { return map_.find(key)->value; }
-  const Value& at(const Key& key) const { return map_.find(key)->value; }
-
-  // Returns a reference to the value associated with the specified key,
-  // creating a new entry if the key is not already present in the map. A
-  // newly-created value will be value-initialized (meaning that it will be
-  // initialized by the default constructor of the value type, if any, or else
-  // will be zero-initialized).
-  Value& operator[](const Key& key) {
-    is_null_ = false;
-    if (!map_.contains(key))
-      map_.add(key, Value());
-    return at(key);
-  }
-
-  // Sets the map to empty (even if previously it was null).
-  void SetToEmpty() {
-    is_null_ = false;
-    map_.clear();
-  }
-
-  // Returns a const reference to the WTF::HashMap managed by this class. If
-  // this object is null, the return value will be an empty map.
-  const WTF::HashMap<Key, Value>& storage() const { return map_; }
-
-  // Passes the underlying storage and resets this map to null.
-  WTF::HashMap<Key, Value> PassStorage() {
-    is_null_ = true;
-    return std::move(map_);
-  }
-
-  // Swaps the contents of this WTFMap with another WTFMap of the same type
-  // (including nullness).
-  void Swap(WTFMap<Key, Value>* other) {
-    std::swap(is_null_, other->is_null_);
-    map_.swap(other->map_);
-  }
-
-  // Swaps the contents of this WTFMap with an WTF::HashMap containing keys and
-  // values of the same type. Since WTF::HashMap cannot represent the null
-  // state, the WTF::HashMap will be empty if WTFMap is null. The WTFMap will
-  // always be left in a non-null state.
-  void Swap(WTF::HashMap<Key, Value>* other) {
-    is_null_ = false;
-    map_.swap(*other);
-  }
-
-  // Returns a new WTFMap that contains a copy of the contents of this map. If
-  // the key/value type defines a Clone() method, it will be used; otherwise
-  // copy constructor/assignment will be used.
-  //
-  // Please note that calling this method will fail compilation if the key/value
-  // type cannot be cloned (which usually means that it is a Mojo handle type or
-  // a type containing Mojo handles).
-  WTFMap Clone() const {
-    WTFMap result;
-    result.is_null_ = is_null_;
-    result.map_ = internal::Clone(map_);
-    return result;
-  }
-
-  // Indicates whether the contents of this map are equal to those of another
-  // WTFMap (including nullness). If the key/value type defines an Equals()
-  // method, it will be used; otherwise == operator will be used.
-  bool Equals(const WTFMap& other) const {
-    if (is_null() != other.is_null())
-      return false;
-    return internal::Equals(map_, other.map_);
-  }
-
-  ConstIterator begin() const { return map_.begin(); }
-  Iterator begin() { return map_.begin(); }
-
-  ConstIterator end() const { return map_.end(); }
-  Iterator end() { return map_.end(); }
-
-  // Returns the iterator pointing to the entry for |key|, if present, or else
-  // returns end().
-  ConstIterator find(const Key& key) const { return map_.find(key); }
-  Iterator find(const Key& key) { return map_.find(key); }
-
-  explicit operator bool() const { return !is_null_; }
-
- private:
-  void Take(WTFMap* other) {
-    operator=(nullptr);
-    Swap(other);
-  }
-
-  WTF::HashMap<Key, Value> map_;
-  bool is_null_;
-
-  DISALLOW_COPY_AND_ASSIGN(WTFMap);
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
index 7fcc2888..7d4cc7b 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -73,7 +73,6 @@
    every use of {Inlined}StructPtr. #}
 #include "mojo/public/cpp/bindings/lib/wtf_hash_util.h"
 #include "mojo/public/cpp/bindings/wtf_array.h"
-#include "mojo/public/cpp/bindings/wtf_map.h"
 #include "third_party/WebKit/Source/wtf/HashFunctions.h"
 #include "third_party/WebKit/Source/wtf/Optional.h"
 #include "third_party/WebKit/Source/wtf/text/WTFString.h"
diff --git a/net/net.gypi b/net/net.gypi
index c83e8eb..8774c56 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -1219,6 +1219,7 @@
       'quic/platform/api/quic_socket_address.cc',
       'quic/platform/api/quic_socket_address.h',
       'quic/platform/api/quic_str_cat.h',
+      'quic/platform/api/quic_text_utils.h',
       'quic/platform/impl/quic_aligned_impl.h',
       'quic/platform/impl/quic_chromium_clock.cc',
       'quic/platform/impl/quic_chromium_clock.h',
@@ -1232,6 +1233,7 @@
       'quic/platform/impl/quic_socket_address_impl.cc',
       'quic/platform/impl/quic_socket_address_impl.h',
       'quic/platform/impl/quic_str_cat_impl.h',
+      'quic/platform/impl/quic_text_utils_impl.h',
       'quic/quartc/quartc_alarm_factory.cc',
       'quic/quartc/quartc_alarm_factory.h',
       'quic/quartc/quartc_factory.cc',
@@ -1968,6 +1970,7 @@
       'quic/platform/api/quic_lru_cache_test.cc',
       'quic/platform/api/quic_reference_counted_test.cc',
       'quic/platform/api/quic_str_cat_test.cc',
+      'quic/platform/api/quic_text_utils_test.cc',
       'quic/platform/impl/quic_chromium_clock_test.cc',
       'quic/quartc/quartc_alarm_factory_test.cc',
       'quic/quartc/quartc_session_test.cc',
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
index 5a838fdb..c6a4efd 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
@@ -33,8 +33,8 @@
 #include "net/quic/core/crypto/quic_decrypter.h"
 #include "net/quic/core/crypto/quic_encrypter.h"
 #include "net/quic/core/quic_connection.h"
-#include "net/quic/core/quic_utils.h"
 #include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/mock_clock.h"
 #include "net/quic/test_tools/mock_random.h"
@@ -473,7 +473,7 @@
     std::unique_ptr<QuicReceivedPacket> packet(maker->MakeDataPacket(
         packet_number, stream_id_, should_include_version, fin, offset, data));
     DVLOG(2) << "packet(" << packet_number << "): " << std::endl
-             << QuicUtils::HexDump(packet->AsStringPiece());
+             << QuicTextUtils::HexDump(packet->AsStringPiece());
     return packet;
   }
 
@@ -499,7 +499,7 @@
                                                    should_include_version, fin,
                                                    offset, data_writes));
     DVLOG(2) << "packet(" << packet_number << "): " << std::endl
-             << QuicUtils::HexDump(packet->AsStringPiece());
+             << QuicTextUtils::HexDump(packet->AsStringPiece());
     return packet;
   }
 
@@ -527,7 +527,7 @@
             packet_number, stream_id, kIncludeVersion, fin, priority,
             std::move(request_headers_), spdy_headers_frame_length, offset));
     DVLOG(2) << "packet(" << packet_number << "): " << std::endl
-             << QuicUtils::HexDump(packet->AsStringPiece());
+             << QuicTextUtils::HexDump(packet->AsStringPiece());
     return packet;
   }
 
@@ -547,7 +547,7 @@
             std::move(request_headers_), header_stream_offset,
             spdy_headers_frame_length, data));
     DVLOG(2) << "packet(" << packet_number << "): " << std::endl
-             << QuicUtils::HexDump(packet->AsStringPiece());
+             << QuicTextUtils::HexDump(packet->AsStringPiece());
     return packet;
   }
 
@@ -603,7 +603,7 @@
         maker->MakeRstPacket(packet_number, !kIncludeVersion, stream_id_,
                              QUIC_STREAM_CANCELLED, bytes_written));
     DVLOG(2) << "packet(" << packet_number << "): " << std::endl
-             << QuicUtils::HexDump(packet->AsStringPiece());
+             << QuicTextUtils::HexDump(packet->AsStringPiece());
     return packet;
   }
 
@@ -631,7 +631,7 @@
         packet_number, should_include_version, stream_id_, largest_received,
         least_unacked, fin, offset, data));
     DVLOG(2) << "packet(" << packet_number << "): " << std::endl
-             << QuicUtils::HexDump(packet->AsStringPiece());
+             << QuicTextUtils::HexDump(packet->AsStringPiece());
     return packet;
   }
 
diff --git a/net/quic/chromium/crypto_test_utils_chromium.cc b/net/quic/chromium/crypto_test_utils_chromium.cc
index 8f7f7e1..a7b8df41 100644
--- a/net/quic/chromium/crypto_test_utils_chromium.cc
+++ b/net/quic/chromium/crypto_test_utils_chromium.cc
@@ -35,7 +35,6 @@
 #include "net/test/test_data_directory.h"
 
 using base::StringPiece;
-using base::StringPrintf;
 using std::string;
 
 namespace net {
diff --git a/net/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc b/net/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
index 4539024..4a04d21 100644
--- a/net/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
+++ b/net/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 
 using base::StringPiece;
@@ -232,14 +233,14 @@
       bool has_pt = test_vectors[j].pt;
 
       // Decode the test vector.
-      string key = QuicUtils::HexDecode(test_vectors[j].key);
-      string iv = QuicUtils::HexDecode(test_vectors[j].iv);
-      string ct = QuicUtils::HexDecode(test_vectors[j].ct);
-      string aad = QuicUtils::HexDecode(test_vectors[j].aad);
-      string tag = QuicUtils::HexDecode(test_vectors[j].tag);
+      string key = QuicTextUtils::HexDecode(test_vectors[j].key);
+      string iv = QuicTextUtils::HexDecode(test_vectors[j].iv);
+      string ct = QuicTextUtils::HexDecode(test_vectors[j].ct);
+      string aad = QuicTextUtils::HexDecode(test_vectors[j].aad);
+      string tag = QuicTextUtils::HexDecode(test_vectors[j].tag);
       string pt;
       if (has_pt) {
-        pt = QuicUtils::HexDecode(test_vectors[j].pt);
+        pt = QuicTextUtils::HexDecode(test_vectors[j].pt);
       }
 
       // The test vector's lengths should look sane. Note that the lengths
diff --git a/net/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc b/net/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
index 037d90f..9236329 100644
--- a/net/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
+++ b/net/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 
 using base::StringPiece;
@@ -178,12 +179,12 @@
     const TestGroupInfo& test_info = test_group_info[i];
     for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
       // Decode the test vector.
-      string key = QuicUtils::HexDecode(test_vectors[j].key);
-      string iv = QuicUtils::HexDecode(test_vectors[j].iv);
-      string pt = QuicUtils::HexDecode(test_vectors[j].pt);
-      string aad = QuicUtils::HexDecode(test_vectors[j].aad);
-      string ct = QuicUtils::HexDecode(test_vectors[j].ct);
-      string tag = QuicUtils::HexDecode(test_vectors[j].tag);
+      string key = QuicTextUtils::HexDecode(test_vectors[j].key);
+      string iv = QuicTextUtils::HexDecode(test_vectors[j].iv);
+      string pt = QuicTextUtils::HexDecode(test_vectors[j].pt);
+      string aad = QuicTextUtils::HexDecode(test_vectors[j].aad);
+      string ct = QuicTextUtils::HexDecode(test_vectors[j].ct);
+      string tag = QuicTextUtils::HexDecode(test_vectors[j].tag);
 
       // The test vector's lengths should look sane. Note that the lengths
       // in |test_info| are in bits.
diff --git a/net/quic/core/crypto/cert_compressor_test.cc b/net/quic/core/crypto/cert_compressor_test.cc
index 9c0e98f..11da033 100644
--- a/net/quic/core/crypto/cert_compressor_test.cc
+++ b/net/quic/core/crypto/cert_compressor_test.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,7 +21,7 @@
   std::vector<string> chain;
   const string compressed = CertCompressor::CompressChain(
       chain, StringPiece(), StringPiece(), nullptr);
-  EXPECT_EQ("00", QuicUtils::HexEncode(compressed));
+  EXPECT_EQ("00", QuicTextUtils::HexEncode(compressed));
 
   std::vector<string> chain2, cached_certs;
   ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr,
@@ -34,7 +35,7 @@
   const string compressed = CertCompressor::CompressChain(
       chain, StringPiece(), StringPiece(), nullptr);
   ASSERT_GE(compressed.size(), 2u);
-  EXPECT_EQ("0100", QuicUtils::HexEncode(compressed.substr(0, 2)));
+  EXPECT_EQ("0100", QuicTextUtils::HexEncode(compressed.substr(0, 2)));
 
   std::vector<string> chain2, cached_certs;
   ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr,
@@ -55,10 +56,10 @@
       StringPiece(), common_sets.get());
   EXPECT_EQ(
       "03"               /* common */
-      "2A00000000000000" /* set hash 42 */
+      "2a00000000000000" /* set hash 42 */
       "01000000"         /* index 1 */
       "00" /* end of list */,
-      QuicUtils::HexEncode(compressed));
+      QuicTextUtils::HexEncode(compressed));
 
   std::vector<string> chain2, cached_certs;
   ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs,
@@ -75,9 +76,9 @@
   const string compressed =
       CertCompressor::CompressChain(chain, StringPiece(), hash_bytes, nullptr);
 
-  EXPECT_EQ("02" /* cached */ + QuicUtils::HexEncode(hash_bytes) +
+  EXPECT_EQ("02" /* cached */ + QuicTextUtils::HexEncode(hash_bytes) +
                 "00" /* end of list */,
-            QuicUtils::HexEncode(compressed));
+            QuicTextUtils::HexEncode(compressed));
 
   std::vector<string> cached_certs, chain2;
   cached_certs.push_back(chain[0]);
@@ -91,37 +92,37 @@
   std::vector<string> cached_certs, chain;
 
   EXPECT_FALSE(CertCompressor::DecompressChain(
-      QuicUtils::HexEncode("04") /* bad entry type */, cached_certs, nullptr,
-      &chain));
-
-  EXPECT_FALSE(CertCompressor::DecompressChain(
-      QuicUtils::HexEncode("01") /* no terminator */, cached_certs, nullptr,
-      &chain));
-
-  EXPECT_FALSE(CertCompressor::DecompressChain(
-      QuicUtils::HexEncode("0200") /* hash truncated */, cached_certs, nullptr,
-      &chain));
-
-  EXPECT_FALSE(CertCompressor::DecompressChain(
-      QuicUtils::HexEncode("0300") /* hash and index truncated */, cached_certs,
+      QuicTextUtils::HexEncode("04") /* bad entry type */, cached_certs,
       nullptr, &chain));
 
+  EXPECT_FALSE(CertCompressor::DecompressChain(
+      QuicTextUtils::HexEncode("01") /* no terminator */, cached_certs, nullptr,
+      &chain));
+
+  EXPECT_FALSE(CertCompressor::DecompressChain(
+      QuicTextUtils::HexEncode("0200") /* hash truncated */, cached_certs,
+      nullptr, &chain));
+
+  EXPECT_FALSE(CertCompressor::DecompressChain(
+      QuicTextUtils::HexEncode("0300") /* hash and index truncated */,
+      cached_certs, nullptr, &chain));
+
   /* without a CommonCertSets */
-  EXPECT_FALSE(
-      CertCompressor::DecompressChain(QuicUtils::HexEncode("03"
-                                                           "0000000000000000"
-                                                           "00000000"),
-                                      cached_certs, nullptr, &chain));
+  EXPECT_FALSE(CertCompressor::DecompressChain(
+      QuicTextUtils::HexEncode("03"
+                               "0000000000000000"
+                               "00000000"),
+      cached_certs, nullptr, &chain));
 
   std::unique_ptr<CommonCertSets> common_sets(
       CryptoTestUtils::MockCommonCertSets("foo", 42, 1));
 
   /* incorrect hash and index */
-  EXPECT_FALSE(
-      CertCompressor::DecompressChain(QuicUtils::HexEncode("03"
-                                                           "a200000000000000"
-                                                           "00000000"),
-                                      cached_certs, nullptr, &chain));
+  EXPECT_FALSE(CertCompressor::DecompressChain(
+      QuicTextUtils::HexEncode("03"
+                               "a200000000000000"
+                               "00000000"),
+      cached_certs, nullptr, &chain));
 }
 
 }  // namespace test
diff --git a/net/quic/core/crypto/chacha20_poly1305_decrypter_test.cc b/net/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
index 94604cb..6caa52fc 100644
--- a/net/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
+++ b/net/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 
 using base::StringPiece;
@@ -142,14 +143,14 @@
     bool has_pt = test_vectors[i].pt;
 
     // Decode the test vector.
-    string key = QuicUtils::HexDecode(test_vectors[i].key);
-    string iv = QuicUtils::HexDecode(test_vectors[i].iv);
-    string fixed = QuicUtils::HexDecode(test_vectors[i].fixed);
-    string aad = QuicUtils::HexDecode(test_vectors[i].aad);
-    string ct = QuicUtils::HexDecode(test_vectors[i].ct);
+    string key = QuicTextUtils::HexDecode(test_vectors[i].key);
+    string iv = QuicTextUtils::HexDecode(test_vectors[i].iv);
+    string fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed);
+    string aad = QuicTextUtils::HexDecode(test_vectors[i].aad);
+    string ct = QuicTextUtils::HexDecode(test_vectors[i].ct);
     string pt;
     if (has_pt) {
-      pt = QuicUtils::HexDecode(test_vectors[i].pt);
+      pt = QuicTextUtils::HexDecode(test_vectors[i].pt);
     }
 
     ChaCha20Poly1305Decrypter decrypter;
diff --git a/net/quic/core/crypto/chacha20_poly1305_encrypter_test.cc b/net/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
index b1c86a5..0ec53087 100644
--- a/net/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
+++ b/net/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
@@ -8,6 +8,7 @@
 
 #include "net/quic/core/crypto/chacha20_poly1305_decrypter.h"
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 
 using base::StringPiece;
@@ -87,7 +88,7 @@
   ChaCha20Poly1305Encrypter encrypter;
   ChaCha20Poly1305Decrypter decrypter;
 
-  string key = QuicUtils::HexDecode(test_vectors[0].key);
+  string key = QuicTextUtils::HexDecode(test_vectors[0].key);
   ASSERT_TRUE(encrypter.SetKey(key));
   ASSERT_TRUE(decrypter.SetKey(key));
   ASSERT_TRUE(encrypter.SetNoncePrefix("abcd"));
@@ -112,12 +113,12 @@
 TEST(ChaCha20Poly1305EncrypterTest, Encrypt) {
   for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
     // Decode the test vector.
-    string key = QuicUtils::HexDecode(test_vectors[i].key);
-    string pt = QuicUtils::HexDecode(test_vectors[i].pt);
-    string iv = QuicUtils::HexDecode(test_vectors[i].iv);
-    string fixed = QuicUtils::HexDecode(test_vectors[i].fixed);
-    string aad = QuicUtils::HexDecode(test_vectors[i].aad);
-    string ct = QuicUtils::HexDecode(test_vectors[i].ct);
+    string key = QuicTextUtils::HexDecode(test_vectors[i].key);
+    string pt = QuicTextUtils::HexDecode(test_vectors[i].pt);
+    string iv = QuicTextUtils::HexDecode(test_vectors[i].iv);
+    string fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed);
+    string aad = QuicTextUtils::HexDecode(test_vectors[i].aad);
+    string ct = QuicTextUtils::HexDecode(test_vectors[i].ct);
 
     ChaCha20Poly1305Encrypter encrypter;
     ASSERT_TRUE(encrypter.SetKey(key));
diff --git a/net/quic/core/crypto/crypto_framer.cc b/net/quic/core/crypto/crypto_framer.cc
index 6462d229..56d9bf8 100644
--- a/net/quic/core/crypto/crypto_framer.cc
+++ b/net/quic/core/crypto/crypto_framer.cc
@@ -4,7 +4,6 @@
 
 #include "net/quic/core/crypto/crypto_framer.h"
 
-#include "base/strings/stringprintf.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
 #include "net/quic/core/quic_data_reader.h"
 #include "net/quic/core/quic_data_writer.h"
diff --git a/net/quic/core/crypto/crypto_handshake_message.cc b/net/quic/core/crypto/crypto_handshake_message.cc
index 74c978f..fcaf59b 100644
--- a/net/quic/core/crypto/crypto_handshake_message.cc
+++ b/net/quic/core/crypto/crypto_handshake_message.cc
@@ -7,13 +7,13 @@
 #include <memory>
 
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "net/quic/core/crypto/crypto_framer.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
 #include "net/quic/core/crypto/crypto_utils.h"
 #include "net/quic/core/quic_socket_address_coder.h"
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::ContainsKey;
 using base::StringPiece;
@@ -234,7 +234,7 @@
         if (it->second.size() == 4) {
           uint32_t value;
           memcpy(&value, it->second.data(), sizeof(value));
-          ret += base::UintToString(value);
+          ret += QuicTextUtils::Uint64ToString(value);
           done = true;
         }
         break;
@@ -243,7 +243,7 @@
         if (it->second.size() == 8) {
           uint64_t value;
           memcpy(&value, it->second.data(), sizeof(value));
-          ret += base::Uint64ToString(value);
+          ret += QuicTextUtils::Uint64ToString(value);
           done = true;
         }
         break;
@@ -319,7 +319,7 @@
     if (!done) {
       // If there's no specific format for this tag, or the value is invalid,
       // then just use hex.
-      ret += "0x" + QuicUtils::HexEncode(it->second);
+      ret += "0x" + QuicTextUtils::HexEncode(it->second);
     }
     ret += "\n";
   }
diff --git a/net/quic/core/crypto/crypto_server_test.cc b/net/quic/core/crypto/crypto_server_test.cc
index cac8d6b..f078524 100644
--- a/net/quic/core/crypto/crypto_server_test.cc
+++ b/net/quic/core/crypto/crypto_server_test.cc
@@ -20,6 +20,7 @@
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_socket_address_coder.h"
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/delayed_verify_strike_register_client.h"
 #include "net/quic/test_tools/mock_clock.h"
@@ -142,8 +143,9 @@
     char public_value[32];
     memset(public_value, 42, sizeof(public_value));
 
-    nonce_hex_ = "#" + QuicUtils::HexEncode(GenerateNonce());
-    pub_hex_ = "#" + QuicUtils::HexEncode(public_value, sizeof(public_value));
+    nonce_hex_ = "#" + QuicTextUtils::HexEncode(GenerateNonce());
+    pub_hex_ =
+        "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
 
     // clang-format off
     CryptoHandshakeMessage client_hello = CryptoTestUtils::Message(
@@ -169,7 +171,7 @@
 
     StringPiece srct;
     ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct));
-    srct_hex_ = "#" + QuicUtils::HexEncode(srct);
+    srct_hex_ = "#" + QuicTextUtils::HexEncode(srct);
 
     StringPiece scfg;
     ASSERT_TRUE(out_.GetStringPiece(kSCFG, &scfg));
@@ -177,7 +179,7 @@
 
     StringPiece scid;
     ASSERT_TRUE(server_config_->GetStringPiece(kSCID, &scid));
-    scid_hex_ = "#" + QuicUtils::HexEncode(scid);
+    scid_hex_ = "#" + QuicTextUtils::HexEncode(scid);
 
     signed_config_ = QuicReferenceCountedPointer<QuicSignedServerConfig>(
         new QuicSignedServerConfig());
@@ -283,12 +285,12 @@
         std::unique_ptr<DiversificationNonce> diversification_nonce,
         std::unique_ptr<ProofSource::Details> proof_source_details) override {
       if (should_succeed_) {
-        ASSERT_EQ(error, QUIC_NO_ERROR) << "Message failed with error "
-                                        << error_details << ": "
-                                        << result_->client_hello.DebugString();
+        ASSERT_EQ(error, QUIC_NO_ERROR)
+            << "Message failed with error " << error_details << ": "
+            << result_->client_hello.DebugString();
       } else {
-        ASSERT_NE(error, QUIC_NO_ERROR) << "Message didn't fail: "
-                                        << result_->client_hello.DebugString();
+        ASSERT_NE(error, QUIC_NO_ERROR)
+            << "Message didn't fail: " << result_->client_hello.DebugString();
 
         EXPECT_TRUE(error_details.find(error_substr_) != string::npos)
             << error_substr_ << " not in " << error_details;
@@ -383,8 +385,8 @@
 
   string XlctHexString() {
     uint64_t xlct = CryptoTestUtils::LeafCertHashForTesting();
-    return "#" +
-           QuicUtils::HexEncode(reinterpret_cast<char*>(&xlct), sizeof(xlct));
+    return "#" + QuicTextUtils::HexEncode(reinterpret_cast<char*>(&xlct),
+                                          sizeof(xlct));
   }
 
  protected:
diff --git a/net/quic/core/crypto/crypto_utils_test.cc b/net/quic/core/crypto/crypto_utils_test.cc
index 1b34c50..0d5bea0 100644
--- a/net/quic/core/crypto/crypto_utils_test.cc
+++ b/net/quic/core/crypto/crypto_utils_test.cc
@@ -5,6 +5,7 @@
 #include "net/quic/core/crypto/crypto_utils.h"
 
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -93,14 +94,15 @@
 
   for (size_t i = 0; i < arraysize(test_vector); i++) {
     // Decode the test vector.
-    string subkey_secret = QuicUtils::HexDecode(test_vector[i].subkey_secret);
-    string label = QuicUtils::HexDecode(test_vector[i].label);
-    string context = QuicUtils::HexDecode(test_vector[i].context);
+    string subkey_secret =
+        QuicTextUtils::HexDecode(test_vector[i].subkey_secret);
+    string label = QuicTextUtils::HexDecode(test_vector[i].label);
+    string context = QuicTextUtils::HexDecode(test_vector[i].context);
     size_t result_len = test_vector[i].result_len;
     bool expect_ok = test_vector[i].expected != nullptr;
     string expected;
     if (expect_ok) {
-      expected = QuicUtils::HexDecode(test_vector[i].expected);
+      expected = QuicTextUtils::HexDecode(test_vector[i].expected);
     }
 
     string result;
diff --git a/net/quic/core/crypto/quic_compressed_certs_cache_test.cc b/net/quic/core/crypto/quic_compressed_certs_cache_test.cc
index 7757364..2cf9746 100644
--- a/net/quic/core/crypto/quic_compressed_certs_cache_test.cc
+++ b/net/quic/core/crypto/quic_compressed_certs_cache_test.cc
@@ -6,8 +6,8 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/crypto/cert_compressor.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -56,10 +56,12 @@
 
   certs_cache_.Insert(chain, common_certs, cached_certs, compressed);
 
-  EXPECT_EQ(nullptr, certs_cache_.GetCompressedCert(
-                         chain, "mismatched common certs", cached_certs));
-  EXPECT_EQ(nullptr, certs_cache_.GetCompressedCert(chain, common_certs,
-                                                    "mismatched cached certs"));
+  EXPECT_EQ(nullptr,
+            certs_cache_.GetCompressedCert(chain, "mismatched common certs",
+                                           cached_certs));
+  EXPECT_EQ(nullptr,
+            certs_cache_.GetCompressedCert(chain, common_certs,
+                                           "mismatched cached certs"));
 
   // A different chain though with equivalent certs should get a cache miss.
   QuicReferenceCountedPointer<ProofSource::Chain> chain2(
@@ -85,7 +87,8 @@
   for (unsigned int i = 0;
        i < QuicCompressedCertsCache::kQuicCompressedCertsCacheSize; i++) {
     EXPECT_EQ(certs_cache_.Size(), i + 1);
-    certs_cache_.Insert(chain, IntToString(i), "", IntToString(i));
+    certs_cache_.Insert(chain, QuicTextUtils::Uint64ToString(i), "",
+                        QuicTextUtils::Uint64ToString(i));
   }
   EXPECT_EQ(certs_cache_.MaxSize(), certs_cache_.Size());
 
diff --git a/net/quic/core/crypto/quic_crypto_client_config.cc b/net/quic/core/crypto/quic_crypto_client_config.cc
index e13ec71d..52e7eed 100644
--- a/net/quic/core/crypto/quic_crypto_client_config.cc
+++ b/net/quic/core/crypto/quic_crypto_client_config.cc
@@ -10,7 +10,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
-#include "base/strings/string_util.h"
 #include "net/quic/core/crypto/cert_compressor.h"
 #include "net/quic/core/crypto/chacha20_poly1305_encrypter.h"
 #include "net/quic/core/crypto/channel_id.h"
@@ -25,6 +24,7 @@
 #include "net/quic/core/crypto/quic_random.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::ContainsKey;
 using base::StringPiece;
@@ -951,8 +951,8 @@
   DCHECK(server_state->IsEmpty());
   size_t i = 0;
   for (; i < canonical_suffixes_.size(); ++i) {
-    if (base::EndsWith(server_id.host(), canonical_suffixes_[i],
-                       base::CompareCase::INSENSITIVE_ASCII)) {
+    if (QuicTextUtils::EndsWithIgnoreCase(server_id.host(),
+                                          canonical_suffixes_[i])) {
       break;
     }
   }
diff --git a/net/quic/core/crypto/quic_crypto_server_config.cc b/net/quic/core/crypto/quic_crypto_server_config.cc
index 95782da25..fa8df9c 100644
--- a/net/quic/core/crypto/quic_crypto_server_config.cc
+++ b/net/quic/core/crypto/quic_crypto_server_config.cc
@@ -37,6 +37,7 @@
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/platform/api/quic_clock.h"
 #include "net/quic/platform/api/quic_reference_counted.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::StringPiece;
 using crypto::SecureHash;
@@ -314,7 +315,7 @@
     if (configs_.find(config->id) != configs_.end()) {
       LOG(WARNING) << "Failed to add config because another with the same "
                       "server config id already exists: "
-                   << QuicUtils::HexEncode(config->id);
+                   << QuicTextUtils::HexEncode(config->id);
       return nullptr;
     }
 
@@ -371,9 +372,9 @@
 
       ConfigMap::iterator it = configs_.find(config->id);
       if (it != configs_.end()) {
-        VLOG(1) << "Keeping scid: " << QuicUtils::HexEncode(config->id)
+        VLOG(1) << "Keeping scid: " << QuicTextUtils::HexEncode(config->id)
                 << " orbit: "
-                << QuicUtils::HexEncode(
+                << QuicTextUtils::HexEncode(
                        reinterpret_cast<const char*>(config->orbit), kOrbitSize)
                 << " new primary_time " << config->primary_time.ToUNIXSeconds()
                 << " old primary_time "
@@ -384,9 +385,9 @@
         it->second->priority = config->priority;
         new_configs.insert(*it);
       } else {
-        VLOG(1) << "Adding scid: " << QuicUtils::HexEncode(config->id)
+        VLOG(1) << "Adding scid: " << QuicTextUtils::HexEncode(config->id)
                 << " orbit: "
-                << QuicUtils::HexEncode(
+                << QuicTextUtils::HexEncode(
                        reinterpret_cast<const char*>(config->orbit), kOrbitSize)
                 << " primary_time " << config->primary_time.ToUNIXSeconds()
                 << " priority " << config->priority;
@@ -1097,7 +1098,7 @@
     primary_config_ = new_primary;
     new_primary->is_primary = true;
     DVLOG(1) << "New primary config.  orbit: "
-             << QuicUtils::HexEncode(
+             << QuicTextUtils::HexEncode(
                     reinterpret_cast<const char*>(primary_config_->orbit),
                     kOrbitSize);
     if (primary_config_changed_cb_.get() != nullptr) {
@@ -1116,10 +1117,10 @@
   primary_config_ = new_primary;
   new_primary->is_primary = true;
   DVLOG(1) << "New primary config.  orbit: "
-           << QuicUtils::HexEncode(
+           << QuicTextUtils::HexEncode(
                   reinterpret_cast<const char*>(primary_config_->orbit),
                   kOrbitSize)
-           << " scid: " << QuicUtils::HexEncode(primary_config_->id);
+           << " scid: " << QuicTextUtils::HexEncode(primary_config_->id);
   next_config_promotion_time_ = QuicWallTime::Zero();
   if (primary_config_changed_cb_.get() != nullptr) {
     primary_config_changed_cb_->Run(primary_config_->id);
diff --git a/net/quic/core/quic_connection.cc b/net/quic/core/quic_connection.cc
index 61c6f8f..8e5a898 100644
--- a/net/quic/core/quic_connection.cc
+++ b/net/quic/core/quic_connection.cc
@@ -19,8 +19,6 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
 #include "net/base/address_family.h"
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
@@ -37,9 +35,9 @@
 #include "net/quic/core/quic_sent_packet_manager.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/platform/api/quic_str_cat.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::StringPiece;
-using base::StringPrintf;
 using std::string;
 
 namespace net {
@@ -1013,9 +1011,10 @@
         ack_queued_ = true;
       } else if (!ack_alarm_->IsSet()) {
         // Wait the minimum of a quarter min_rtt and the delayed ack time.
-        QuicTime::Delta ack_delay = std::min(
-            DelayedAckTime(), sent_packet_manager_->GetRttStats()->min_rtt() *
-                                  ack_decimation_delay_);
+        QuicTime::Delta ack_delay =
+            std::min(DelayedAckTime(),
+                     sent_packet_manager_->GetRttStats()->min_rtt() *
+                         ack_decimation_delay_);
         ack_alarm_->Set(clock_->ApproximateNow() + ack_delay);
       }
     } else {
@@ -1577,7 +1576,7 @@
            << QuicUtils::EncryptionLevelToString(packet->encryption_level)
            << ", encrypted length:" << encrypted_length;
   DVLOG(2) << ENDPOINT << "packet(" << packet_number << "): " << std::endl
-           << QuicUtils::HexDump(
+           << QuicTextUtils::HexDump(
                   StringPiece(packet->encrypted_buffer, encrypted_length));
 
   // Measure the RTT from before the write begins to avoid underestimating the
diff --git a/net/quic/core/quic_connection_test.cc b/net/quic/core/quic_connection_test.cc
index ed3f5b3..007853dc 100644
--- a/net/quic/core/quic_connection_test.cc
+++ b/net/quic/core/quic_connection_test.cc
@@ -12,7 +12,6 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
-#include "base/strings/stringprintf.h"
 #include "net/base/net_errors.h"
 #include "net/quic/core/congestion_control/loss_detection_interface.h"
 #include "net/quic/core/congestion_control/send_algorithm_interface.h"
diff --git a/net/quic/core/quic_crypto_client_stream.cc b/net/quic/core/quic_crypto_client_stream.cc
index 732e23a7..0f48035 100644
--- a/net/quic/core/quic_crypto_client_stream.cc
+++ b/net/quic/core/quic_crypto_client_stream.cc
@@ -9,7 +9,6 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/strings/stringprintf.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
 #include "net/quic/core/crypto/crypto_utils.h"
 #include "net/quic/core/crypto/null_encrypter.h"
diff --git a/net/quic/core/quic_crypto_server_stream.cc b/net/quic/core/quic_crypto_server_stream.cc
index f1b6041..6bf69511 100644
--- a/net/quic/core/quic_crypto_server_stream.cc
+++ b/net/quic/core/quic_crypto_server_stream.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "base/base64.h"
 #include "crypto/secure_hash.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
 #include "net/quic/core/crypto/crypto_utils.h"
@@ -17,6 +16,7 @@
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/core/quic_session.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::StringPiece;
 using std::string;
@@ -421,19 +421,7 @@
   uint8_t digest[32];
   hash->Finish(digest, sizeof(digest));
 
-  base::Base64Encode(
-      string(reinterpret_cast<const char*>(digest), sizeof(digest)), output);
-  // Remove padding.
-  size_t len = output->size();
-  if (len >= 2) {
-    if ((*output)[len - 1] == '=') {
-      len--;
-      if ((*output)[len - 1] == '=') {
-        len--;
-      }
-      output->resize(len);
-    }
-  }
+  QuicTextUtils::Base64Encode(digest, arraysize(digest), output);
   return true;
 }
 
diff --git a/net/quic/core/quic_flow_controller.cc b/net/quic/core/quic_flow_controller.cc
index b789823..318aea35 100644
--- a/net/quic/core/quic_flow_controller.cc
+++ b/net/quic/core/quic_flow_controller.cc
@@ -6,7 +6,6 @@
 
 #include <cstdint>
 
-#include "base/strings/stringprintf.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_connection.h"
 #include "net/quic/core/quic_packets.h"
diff --git a/net/quic/core/quic_flow_controller_test.cc b/net/quic/core/quic_flow_controller_test.cc
index b3b166ff..012c1ea 100644
--- a/net/quic/core/quic_flow_controller_test.cc
+++ b/net/quic/core/quic_flow_controller_test.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "base/format_macros.h"
-#include "base/strings/stringprintf.h"
 #include "net/quic/platform/api/quic_str_cat.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
 #include "net/quic/test_tools/quic_flow_controller_peer.h"
diff --git a/net/quic/core/quic_headers_stream.cc b/net/quic/core/quic_headers_stream.cc
index 81e9886..cedcb34b 100644
--- a/net/quic/core/quic_headers_stream.cc
+++ b/net/quic/core/quic_headers_stream.cc
@@ -11,8 +11,6 @@
 
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_header_list.h"
diff --git a/net/quic/core/quic_headers_stream_test.cc b/net/quic/core/quic_headers_stream_test.cc
index 2a5e6b13..e3e0524 100644
--- a/net/quic/core/quic_headers_stream_test.cc
+++ b/net/quic/core/quic_headers_stream_test.cc
@@ -10,7 +10,6 @@
 #include <tuple>
 #include <utility>
 
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_utils.h"
diff --git a/net/quic/core/quic_multipath_sent_packet_manager.cc b/net/quic/core/quic_multipath_sent_packet_manager.cc
index d6366ac0..e0173900 100644
--- a/net/quic/core/quic_multipath_sent_packet_manager.cc
+++ b/net/quic/core/quic_multipath_sent_packet_manager.cc
@@ -6,7 +6,6 @@
 
 #include <cstdint>
 
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/platform/api/quic_str_cat.h"
 
@@ -327,8 +326,8 @@
     if (debug_state.empty()) {
       continue;
     }
-    debug_state_by_path =
-        debug_state_by_path + "[" + base::IntToString(i) + "]:" + debug_state;
+    debug_state_by_path = QuicStrCat(
+        debug_state_by_path, "[", i, "]:", debug_state);
   }
   return debug_state_by_path;
 }
diff --git a/net/quic/core/quic_packets.cc b/net/quic/core/quic_packets.cc
index 2c7eb0e..a67cfa6 100644
--- a/net/quic/core/quic_packets.cc
+++ b/net/quic/core/quic_packets.cc
@@ -8,6 +8,8 @@
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/core/quic_versions.h"
+#include "net/quic/platform/api/quic_str_cat.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::StringPiece;
 using std::string;
@@ -97,8 +99,9 @@
   }
   if (header.public_header.nonce != nullptr) {
     os << ", diversification_nonce: "
-       << QuicUtils::HexEncode(StringPiece(header.public_header.nonce->data(),
-                                           header.public_header.nonce->size()));
+       << QuicTextUtils::HexEncode(
+              StringPiece(header.public_header.nonce->data(),
+                          header.public_header.nonce->size()));
   }
   os << ", path_id: " << static_cast<int>(header.path_id)
      << ", packet_number: " << header.packet_number << " }\n";
diff --git a/net/quic/core/quic_sent_packet_manager.cc b/net/quic/core/quic_sent_packet_manager.cc
index a00d680..8872f461 100644
--- a/net/quic/core/quic_sent_packet_manager.cc
+++ b/net/quic/core/quic_sent_packet_manager.cc
@@ -17,7 +17,7 @@
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_connection_stats.h"
 #include "net/quic/core/quic_flags.h"
-
+#include "net/quic/core/quic_pending_retransmission.h"
 
 namespace net {
 
@@ -867,8 +867,9 @@
                  static_cast<int64_t>(0.5 * srtt.ToMilliseconds())));
   }
   if (!unacked_packets_.HasMultipleInFlightPackets()) {
-    return std::max(2 * srtt, 1.5 * srtt + QuicTime::Delta::FromMilliseconds(
-                                               kMinRetransmissionTimeMs / 2));
+    return std::max(2 * srtt,
+                    1.5 * srtt + QuicTime::Delta::FromMilliseconds(
+                                     kMinRetransmissionTimeMs / 2));
   }
   return QuicTime::Delta::FromMilliseconds(
       std::max(kMinTailLossProbeTimeoutMs,
diff --git a/net/quic/core/quic_session.cc b/net/quic/core/quic_session.cc
index 29d8de2..255c05e 100644
--- a/net/quic/core/quic_session.cc
+++ b/net/quic/core/quic_session.cc
@@ -6,7 +6,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/crypto/proof_verifier.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_connection.h"
@@ -14,10 +13,8 @@
 #include "net/quic/core/quic_flow_controller.h"
 #include "net/quic/platform/api/quic_str_cat.h"
 
-using base::IntToString;
 using base::StringPiece;
 using std::string;
-using net::SpdyPriority;
 
 namespace net {
 
diff --git a/net/quic/core/quic_session_test.cc b/net/quic/core/quic_session_test.cc
index cc304cdb..a77ec64 100644
--- a/net/quic/core/quic_session_test.cc
+++ b/net/quic/core/quic_session_test.cc
@@ -10,7 +10,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/rand_util.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
 #include "net/quic/core/crypto/null_encrypter.h"
diff --git a/net/quic/core/quic_spdy_stream.cc b/net/quic/core/quic_spdy_stream.cc
index b55ca87..9913738 100644
--- a/net/quic/core/quic_spdy_stream.cc
+++ b/net/quic/core/quic_spdy_stream.cc
@@ -7,13 +7,13 @@
 #include <utility>
 
 #include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/base/parse_number.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_spdy_session.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/core/quic_write_blocked_list.h"
 #include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::IntToString;
 using base::StringPiece;
@@ -91,9 +91,10 @@
   // trailers may be processed out of order at the peer.
   DVLOG(1) << "Inserting trailer: (" << kFinalOffsetHeaderKey << ", "
            << stream_bytes_written() + queued_data_bytes() << ")";
-  trailer_block.insert(std::make_pair(
-      kFinalOffsetHeaderKey,
-      IntToString(stream_bytes_written() + queued_data_bytes())));
+  trailer_block.insert(
+      std::make_pair(kFinalOffsetHeaderKey,
+                     QuicTextUtils::Uint64ToString(stream_bytes_written() +
+                                                   queued_data_bytes())));
 
   // Write the trailing headers with a FIN, and close stream for writing:
   // trailers are the last thing to be sent on a stream.
diff --git a/net/quic/core/quic_spdy_stream_test.cc b/net/quic/core/quic_spdy_stream_test.cc
index 284ed864..77704fa6 100644
--- a/net/quic/core/quic_spdy_stream_test.cc
+++ b/net/quic/core/quic_spdy_stream_test.cc
@@ -8,11 +8,11 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/quic_connection.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/core/quic_write_blocked_list.h"
 #include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/quic_flow_controller_peer.h"
 #include "net/quic/test_tools/quic_session_peer.h"
 #include "net/quic/test_tools/quic_stream_peer.h"
@@ -544,9 +544,10 @@
   EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId1, _)).Times(0);
   EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId2, _)).Times(0);
   EXPECT_CALL(*connection_,
-              SendWindowUpdate(0, QuicFlowControllerPeer::ReceiveWindowOffset(
-                                      session_->flow_controller()) +
-                                      1 + kWindow / 2));
+              SendWindowUpdate(0,
+                               QuicFlowControllerPeer::ReceiveWindowOffset(
+                                   session_->flow_controller()) +
+                                   1 + kWindow / 2));
   QuicStreamFrame frame3(kClientDataStreamId1, false, (kWindow / 4),
                          StringPiece("a"));
   stream_->OnStreamFrame(frame3);
@@ -776,7 +777,8 @@
   trailers_block["key1"] = "value1";
   trailers_block["key2"] = "value2";
   trailers_block["key3"] = "value3";
-  trailers_block[kFinalOffsetHeaderKey] = base::IntToString(body.size());
+  trailers_block[kFinalOffsetHeaderKey] =
+      QuicTextUtils::Uint64ToString(body.size());
 
   QuicHeaderList trailers = ProcessHeaders(true, trailers_block);
 
@@ -858,7 +860,8 @@
   SpdyHeaderBlock trailers;
   trailers["trailer key"] = "trailer value";
   SpdyHeaderBlock trailers_with_offset(trailers.Clone());
-  trailers_with_offset[kFinalOffsetHeaderKey] = base::Uint64ToString(kBodySize);
+  trailers_with_offset[kFinalOffsetHeaderKey] =
+      QuicTextUtils::Uint64ToString(kBodySize);
   EXPECT_CALL(*session_, WriteHeadersMock(_, _, true, _, _));
   stream_->WriteTrailers(std::move(trailers), nullptr);
   EXPECT_EQ(trailers_with_offset, session_->GetWriteHeaders());
diff --git a/net/quic/core/quic_stream_sequencer.cc b/net/quic/core/quic_stream_sequencer.cc
index a671767..f4b7143 100644
--- a/net/quic/core/quic_stream_sequencer.cc
+++ b/net/quic/core/quic_stream_sequencer.cc
@@ -11,8 +11,6 @@
 
 #include "base/format_macros.h"
 #include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_packets.h"
@@ -22,9 +20,7 @@
 #include "net/quic/platform/api/quic_clock.h"
 #include "net/quic/platform/api/quic_str_cat.h"
 
-using base::IntToString;
 using base::StringPiece;
-using base::StringPrintf;
 using std::string;
 
 namespace net {
diff --git a/net/quic/core/quic_stream_sequencer_buffer.cc b/net/quic/core/quic_stream_sequencer_buffer.cc
index 7c56c295..074e124 100644
--- a/net/quic/core/quic_stream_sequencer_buffer.cc
+++ b/net/quic/core/quic_stream_sequencer_buffer.cc
@@ -6,13 +6,10 @@
 
 #include "base/format_macros.h"
 #include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/platform/api/quic_str_cat.h"
 
-using base::StringPrintf;
 using std::string;
 
 namespace net {
diff --git a/net/quic/core/quic_tag.cc b/net/quic/core/quic_tag.cc
index 584f582..1bb9335 100644
--- a/net/quic/core/quic_tag.cc
+++ b/net/quic/core/quic_tag.cc
@@ -7,7 +7,7 @@
 #include <algorithm>
 
 #include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 namespace net {
 
@@ -56,7 +56,7 @@
     return std::string(chars, sizeof(chars));
   }
 
-  return base::UintToString(orig_tag);
+  return QuicTextUtils::Uint64ToString(orig_tag);
 }
 
 uint32_t MakeQuicTag(char a, char b, char c, char d) {
diff --git a/net/quic/core/quic_utils.cc b/net/quic/core/quic_utils.cc
index e0e69b69..cf51612 100644
--- a/net/quic/core/quic_utils.cc
+++ b/net/quic/core/quic_utils.cc
@@ -12,9 +12,6 @@
 
 #include "base/containers/adapters.h"
 #include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
 #include "net/quic/core/quic_constants.h"
 #include "net/quic/core/quic_flags.h"
 
@@ -228,56 +225,4 @@
   return IPV4_TO_IPV4_CHANGE;
 }
 
-string QuicUtils::HexEncode(const char* data, size_t length) {
-  return HexEncode(StringPiece(data, length));
-}
-
-string QuicUtils::HexEncode(StringPiece data) {
-  return ::base::HexEncode(data.data(), data.size());
-}
-
-string QuicUtils::HexDecode(StringPiece data) {
-  if (data.empty())
-    return "";
-  std::vector<uint8_t> v;
-  if (!base::HexStringToBytes(data.as_string(), &v))
-    return "";
-  string out;
-  if (!v.empty())
-    out.assign(reinterpret_cast<const char*>(&v[0]), v.size());
-  return out;
-}
-
-string QuicUtils::HexDump(StringPiece binary_input) {
-  int offset = 0;
-  const int kBytesPerLine = 16;  // Max bytes dumped per line
-  const char* buf = binary_input.data();
-  int bytes_remaining = binary_input.size();
-  string s;  // our output
-  const char* p = buf;
-  while (bytes_remaining > 0) {
-    const int line_bytes = std::min(bytes_remaining, kBytesPerLine);
-    base::StringAppendF(&s, "0x%04x:  ", offset);  // Do the line header
-    for (int i = 0; i < kBytesPerLine; ++i) {
-      if (i < line_bytes) {
-        base::StringAppendF(&s, "%02x", static_cast<unsigned char>(p[i]));
-      } else {
-        s += "  ";  // two-space filler instead of two-space hex digits
-      }
-      if (i % 2)
-        s += ' ';
-    }
-    s += ' ';
-    for (int i = 0; i < line_bytes; ++i) {  // Do the ASCII dump
-      s += (p[i] > 32 && p[i] < 127) ? p[i] : '.';
-    }
-
-    bytes_remaining -= line_bytes;
-    offset += line_bytes;
-    p += line_bytes;
-    s += '\n';
-  }
-  return s;
-}
-
 }  // namespace net
diff --git a/net/quic/core/quic_utils.h b/net/quic/core/quic_utils.h
index db8e834..c32e865 100644
--- a/net/quic/core/quic_utils.h
+++ b/net/quic/core/quic_utils.h
@@ -65,22 +65,6 @@
       const QuicSocketAddress& old_address,
       const QuicSocketAddress& new_address);
 
-  // This converts |length| bytes of binary to a 2*|length|-character
-  // hexadecimal representation.
-  // Return value: 2*|length| characters of ASCII std::string.
-  static std::string HexEncode(const char* data, size_t length);
-  static std::string HexEncode(base::StringPiece data);
-
-  // Converts |data| from a hexadecimal ASCII std::string to binary.
-  static std::string HexDecode(base::StringPiece data);
-
-  // Returns a std::string containing hex and ASCII representations of |binary|,
-  // side-by-side in the style of hexdump. Non-printable characters will be
-  // printed as '.' in the ASCII output.
-  // For example:
-  // "0x0000:  4865 6c6c 6f2c 2051 5549 4321 0102 0304  Hello,.QUIC!...."
-  static std::string HexDump(base::StringPiece binary_data);
-
  private:
   DISALLOW_COPY_AND_ASSIGN(QuicUtils);
 };
diff --git a/net/quic/core/quic_utils_test.cc b/net/quic/core/quic_utils_test.cc
index a6d794e..6d0be398b 100644
--- a/net/quic/core/quic_utils_test.cc
+++ b/net/quic/core/quic_utils_test.cc
@@ -99,27 +99,6 @@
                 reinterpret_cast<const char*>(data.data()), data.size())));
 }
 
-TEST(QuicUtilsTest, HexDump) {
-  // Verify output of the HexDump method is as expected.
-  char packet[] = {
-      0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x51, 0x55, 0x49, 0x43, 0x21,
-      0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
-      0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x6c,
-      0x6f, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74,
-      0x6f, 0x20, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69,
-      0x70, 0x6c, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x6f, 0x66,
-      0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x01, 0x02, 0x03, 0x00,
-  };
-  EXPECT_EQ(
-      QuicUtils::HexDump(packet),
-      "0x0000:  4865 6c6c 6f2c 2051 5549 4321 2054 6869  Hello,.QUIC!.Thi\n"
-      "0x0010:  7320 7374 7269 6e67 2073 686f 756c 6420  s.string.should.\n"
-      "0x0020:  6265 206c 6f6e 6720 656e 6f75 6768 2074  be.long.enough.t\n"
-      "0x0030:  6f20 7370 616e 206d 756c 7469 706c 6520  o.span.multiple.\n"
-      "0x0040:  6c69 6e65 7320 6f66 206f 7574 7075 742e  lines.of.output.\n"
-      "0x0050:  0102 03                                  ...\n");
-}
-
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/core/quic_versions.cc b/net/quic/core/quic_versions.cc
index fc661d14..bb02c4d86 100644
--- a/net/quic/core/quic_versions.cc
+++ b/net/quic/core/quic_versions.cc
@@ -5,7 +5,7 @@
 #include "net/quic/core/quic_versions.h"
 
 #include "base/memory/ptr_util.h"
-#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "net/quic/core/quic_error_codes.h"
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_tag.h"
diff --git a/net/quic/core/spdy_utils.cc b/net/quic/core/spdy_utils.cc
index f551696..c2f4fd2 100644
--- a/net/quic/core/spdy_utils.cc
+++ b/net/quic/core/spdy_utils.cc
@@ -8,10 +8,7 @@
 #include <vector>
 
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/spdy/spdy_flags.h"
 #include "net/spdy/spdy_frame_builder.h"
 #include "net/spdy/spdy_framer.h"
@@ -61,10 +58,9 @@
   } else {
     // Check whether multiple values are consistent.
     StringPiece content_length_header = it->second;
-    std::vector<string> values =
-        base::SplitString(content_length_header, base::StringPiece("\0", 1),
-                          base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-    for (const string& value : values) {
+    std::vector<StringPiece> values =
+        QuicTextUtils::Split(content_length_header, '\0');
+    for (const StringPiece& value : values) {
       int64_t new_value;
       if (!base::StringToInt64(value, &new_value) || new_value < 0) {
         DLOG(ERROR) << "Content length was either unparseable or negative.";
@@ -110,9 +106,9 @@
 
   // Trailers must not have empty keys, and must not contain pseudo headers.
   for (const auto& trailer : *trailers) {
-    base::StringPiece key = trailer.first;
-    base::StringPiece value = trailer.second;
-    if (base::StartsWith(key, ":", base::CompareCase::INSENSITIVE_ASCII)) {
+    StringPiece key = trailer.first;
+    StringPiece value = trailer.second;
+    if (QuicTextUtils::StartsWith(key, ":")) {
       DVLOG(1) << "Trailers must not contain pseudo-header: '" << key << "','"
                << value << "'.";
       return false;
@@ -135,7 +131,7 @@
       return false;
     }
 
-    if (std::any_of(name.begin(), name.end(), base::IsAsciiUpper<char>)) {
+    if (QuicTextUtils::ContainsUpperCase(name)) {
       DVLOG(1) << "Malformed header: Header name " << name
                << " contains upper-case characters.";
       return false;
@@ -176,7 +172,7 @@
       return false;
     }
 
-    if (std::any_of(name.begin(), name.end(), base::IsAsciiUpper<char>)) {
+    if (QuicTextUtils::ContainsUpperCase(name)) {
       DVLOG(1) << "Malformed header: Header name " << name
                << " contains upper-case characters.";
       return false;
diff --git a/net/quic/core/spdy_utils_test.cc b/net/quic/core/spdy_utils_test.cc
index c07f9de..75823c3d 100644
--- a/net/quic/core/spdy_utils_test.cc
+++ b/net/quic/core/spdy_utils_test.cc
@@ -4,9 +4,10 @@
 #include "net/quic/core/spdy_utils.h"
 
 #include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 using base::StringPiece;
 using std::string;
@@ -26,7 +27,8 @@
   input_headers[":pseudo2"] = "pseudo value2";
   input_headers["key1"] = "value1";
   const int64_t kContentLength = 1234;
-  input_headers["content-length"] = base::Int64ToString(kContentLength);
+  input_headers["content-length"] =
+      QuicTextUtils::Uint64ToString(kContentLength);
   input_headers["key2"] = "value2";
 
   // Serialize the header block.
@@ -55,7 +57,8 @@
   input_headers[":pseudo2"] = "pseudo value2";
   input_headers["key1"] = "value1";
   const int64_t kContentLength = 12345678900;
-  input_headers["content-length"] = base::Int64ToString(kContentLength);
+  input_headers["content-length"] =
+      QuicTextUtils::Uint64ToString(kContentLength);
   input_headers["key2"] = "value2";
 
   // Serialize the header block.
@@ -80,7 +83,8 @@
   // result is the same as the trailers that the test started with.
   SpdyHeaderBlock input_trailers;
   const size_t kFinalOffset = 5678;
-  input_trailers[kFinalOffsetHeaderKey] = base::IntToString(kFinalOffset);
+  input_trailers[kFinalOffsetHeaderKey] =
+      QuicTextUtils::Uint64ToString(kFinalOffset);
   input_trailers["key1"] = "value1";
   input_trailers["key2"] = "value2";
 
diff --git a/net/quic/platform/api/quic_text_utils.h b/net/quic/platform/api/quic_text_utils.h
new file mode 100644
index 0000000..7eabe73
--- /dev/null
+++ b/net/quic/platform/api/quic_text_utils.h
@@ -0,0 +1,98 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_PLATFORM_API_QUIC_TEXT_UTILS_H_
+#define NET_QUIC_PLATFORM_API_QUIC_TEXT_UTILS_H_
+
+#include "base/strings/string_piece.h"
+#include "net/quic/platform/impl/quic_text_utils_impl.h"
+
+namespace net {
+
+// Various utilities for manipulating text.
+class QuicTextUtils {
+ public:
+  // Returns true if |data| starts with |prefix|, case sensitively.
+  static bool StartsWith(base::StringPiece data, base::StringPiece prefix) {
+    return QuicTextUtilsImpl::StartsWith(data, prefix);
+  }
+
+  // Returns true if |data| ends with |suffix|, case insensitively.
+  static bool EndsWithIgnoreCase(base::StringPiece data,
+                                 base::StringPiece suffix) {
+    return QuicTextUtilsImpl::EndsWithIgnoreCase(data, suffix);
+  }
+
+  // Returns a new string in which |data| has been converted to lower case.
+  static std::string ToLower(base::StringPiece data) {
+    return QuicTextUtilsImpl::ToLower(data);
+  }
+
+  // Removes leading and trailing whitespace from |data|.
+  static void RemoveLeadingAndTrailingWhitespace(base::StringPiece* data) {
+    QuicTextUtilsImpl::RemoveLeadingAndTrailingWhitespace(data);
+  }
+
+  // Returns true if |in| represents a valid uint64, and stores that value in
+  // |out|.
+  static bool StringToUint64(base::StringPiece in, uint64_t* out) {
+    return QuicTextUtilsImpl::StringToUint64(in, out);
+  }
+
+  // Returns a new string representing |in|.
+  static std::string Uint64ToString(uint64_t in) {
+    return QuicTextUtilsImpl::Uint64ToString(in);
+  }
+
+  // This converts |length| bytes of binary to a 2*|length|-character
+  // hexadecimal representation.
+  // Return value: 2*|length| characters of ASCII string.
+  static std::string HexEncode(const char* data, size_t length) {
+    return HexEncode(base::StringPiece(data, length));
+  }
+
+  // This converts |data.length()| bytes of binary to a
+  // 2*|data.length()|-character hexadecimal representation.
+  // Return value: 2*|data.length()| characters of ASCII string.
+  static std::string HexEncode(base::StringPiece data) {
+    return QuicTextUtilsImpl::HexEncode(data);
+  }
+
+  // Converts |data| from a hexadecimal ASCII string to a binary string
+  // that is |data.length()/2| bytes long.
+  static std::string HexDecode(base::StringPiece data) {
+    return QuicTextUtilsImpl::HexDecode(data);
+  }
+
+  // Base64 encodes with no padding |data_len| bytes of |data| into |output|.
+  static void Base64Encode(const uint8_t* data,
+                           size_t data_len,
+                           std::string* output) {
+    return QuicTextUtilsImpl::Base64Encode(data, data_len, output);
+  }
+
+  // Returns a string containing hex and ASCII representations of |binary|,
+  // side-by-side in the style of hexdump. Non-printable characters will be
+  // printed as '.' in the ASCII output.
+  // For example, given the input "Hello, QUIC!\01\02\03\04", returns:
+  // "0x0000:  4865 6c6c 6f2c 2051 5549 4321 0102 0304  Hello,.QUIC!...."
+  static std::string HexDump(base::StringPiece binary_data) {
+    return QuicTextUtilsImpl::HexDump(binary_data);
+  }
+
+  // Returns true if |data| contains any uppercase characters.
+  static bool ContainsUpperCase(base::StringPiece data) {
+    return QuicTextUtilsImpl::ContainsUpperCase(data);
+  }
+
+  // Splits |data| into a vector of pieces delimited by |delim|.
+  static std::vector<base::StringPiece> Split(base::StringPiece data,
+                                              char delim) {
+    return QuicTextUtilsImpl::Split(data, delim);
+  }
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_PLATFORM_API_QUIC_TEXT_UTILS_H_
diff --git a/net/quic/platform/api/quic_text_utils_test.cc b/net/quic/platform/api/quic_text_utils_test.cc
new file mode 100644
index 0000000..c424a36d
--- /dev/null
+++ b/net/quic/platform/api/quic_text_utils_test.cc
@@ -0,0 +1,138 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/platform/api/quic_text_utils.h"
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+
+TEST(QuicTextUtilsText, StartsWith) {
+  EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", "hello"));
+  EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", "hello world"));
+  EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", ""));
+  EXPECT_FALSE(QuicTextUtils::StartsWith("hello world", "Hello"));
+  EXPECT_FALSE(QuicTextUtils::StartsWith("hello world", "world"));
+  EXPECT_FALSE(QuicTextUtils::StartsWith("hello world", "bar"));
+}
+
+TEST(QuicTextUtilsText, EndsWithIgnoreCase) {
+  EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", "world"));
+  EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", "hello world"));
+  EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", ""));
+  EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", "WORLD"));
+  EXPECT_FALSE(QuicTextUtils::EndsWithIgnoreCase("hello world", "hello"));
+}
+
+TEST(QuicTextUtilsText, ToLower) {
+  EXPECT_EQ("lower", QuicTextUtils::ToLower("LOWER"));
+  EXPECT_EQ("lower", QuicTextUtils::ToLower("lower"));
+  EXPECT_EQ("lower", QuicTextUtils::ToLower("lOwEr"));
+  EXPECT_EQ("123", QuicTextUtils::ToLower("123"));
+  EXPECT_EQ("", QuicTextUtils::ToLower(""));
+}
+
+TEST(QuicTextUtilsText, RemoveLeadingAndTrailingWhitespace) {
+  string input;
+
+  for (auto input : {"text", " text", "  text", "text ", "text  ", " text ",
+                     "  text  ", "\r\n\ttext", "text\n\r\t"}) {
+    StringPiece piece(input);
+    QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&piece);
+    EXPECT_EQ("text", piece);
+  }
+}
+
+TEST(QuicTextUtilsText, StringToUint64) {
+  uint64_t val = 0;
+  EXPECT_TRUE(QuicTextUtils::StringToUint64("123", &val));
+  EXPECT_EQ(123u, val);
+  EXPECT_TRUE(QuicTextUtils::StringToUint64("1234", &val));
+  EXPECT_EQ(1234u, val);
+  EXPECT_FALSE(QuicTextUtils::StringToUint64("", &val));
+  EXPECT_FALSE(QuicTextUtils::StringToUint64("-123", &val));
+  EXPECT_FALSE(QuicTextUtils::StringToUint64("-123.0", &val));
+}
+
+TEST(QuicTextUtilsText, Uint64ToString) {
+  EXPECT_EQ("123", QuicTextUtils::Uint64ToString(123));
+  EXPECT_EQ("1234", QuicTextUtils::Uint64ToString(1234));
+}
+
+TEST(QuicTextUtilsText, HexEncode) {
+  EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello", 5));
+  EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello World", 5));
+  EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello"));
+}
+
+TEST(QuicTextUtilsText, HexDecode) {
+  EXPECT_EQ("Hello", QuicTextUtils::HexDecode("48656c6c6f"));
+  EXPECT_EQ("", QuicTextUtils::HexDecode(""));
+}
+
+TEST(QuicTextUtilsText, HexDump) {
+  // Verify output of the HexDump method is as expected.
+  char packet[] = {
+      0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x51, 0x55, 0x49, 0x43, 0x21,
+      0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+      0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x6c,
+      0x6f, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74,
+      0x6f, 0x20, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69,
+      0x70, 0x6c, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x6f, 0x66,
+      0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x01, 0x02, 0x03, 0x00,
+  };
+  EXPECT_EQ(
+      QuicTextUtils::HexDump(packet),
+      "0x0000:  4865 6c6c 6f2c 2051 5549 4321 2054 6869  Hello,.QUIC!.Thi\n"
+      "0x0010:  7320 7374 7269 6e67 2073 686f 756c 6420  s.string.should.\n"
+      "0x0020:  6265 206c 6f6e 6720 656e 6f75 6768 2074  be.long.enough.t\n"
+      "0x0030:  6f20 7370 616e 206d 756c 7469 706c 6520  o.span.multiple.\n"
+      "0x0040:  6c69 6e65 7320 6f66 206f 7574 7075 742e  lines.of.output.\n"
+      "0x0050:  0102 03                                  ...\n");
+}
+
+TEST(QuicTextUtilsText, Base64Encode) {
+  string output;
+  string input = "Hello";
+  QuicTextUtils::Base64Encode(reinterpret_cast<const uint8_t*>(input.data()),
+                              input.length(), &output);
+  EXPECT_EQ("SGVsbG8", output);
+
+  input =
+      "Hello, QUIC! This string should be long enough to span"
+      "multiple lines of output\n";
+  QuicTextUtils::Base64Encode(reinterpret_cast<const uint8_t*>(input.data()),
+                              input.length(), &output);
+  EXPECT_EQ(
+      "SGVsbG8sIFFVSUMhIFRoaXMgc3RyaW5nIHNob3VsZCBiZSBsb25n"
+      "IGVub3VnaCB0byBzcGFubXVsdGlwbGUgbGluZXMgb2Ygb3V0cHV0Cg",
+      output);
+}
+
+TEST(QuicTextUtilsText, ContainsUpperCase) {
+  EXPECT_FALSE(QuicTextUtils::ContainsUpperCase("abc"));
+  EXPECT_FALSE(QuicTextUtils::ContainsUpperCase(""));
+  EXPECT_FALSE(QuicTextUtils::ContainsUpperCase("123"));
+  EXPECT_TRUE(QuicTextUtils::ContainsUpperCase("ABC"));
+  EXPECT_TRUE(QuicTextUtils::ContainsUpperCase("aBc"));
+}
+
+TEST(QuicTextUtilsText, Split) {
+  EXPECT_EQ(std::vector<StringPiece>({"a", "b", "c"}),
+            QuicTextUtils::Split("a,b,c", ','));
+  EXPECT_EQ(std::vector<StringPiece>({"a", "b", "c"}),
+            QuicTextUtils::Split("a:b:c", ':'));
+  EXPECT_EQ(std::vector<StringPiece>({"a:b:c"}),
+            QuicTextUtils::Split("a:b:c", ','));
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/quic/platform/impl/quic_text_utils_impl.h b/net/quic/platform/impl/quic_text_utils_impl.h
new file mode 100644
index 0000000..a3cce167
--- /dev/null
+++ b/net/quic/platform/impl/quic_text_utils_impl.h
@@ -0,0 +1,146 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_PLATFORM_IMPL_QUIC_TEXT_UTILS_IMPL_H_
+#define NET_QUIC_PLATFORM_IMPL_QUIC_TEXT_UTILS_IMPL_H_
+
+#include <algorithm>
+
+#include "base/base64.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace net {
+
+// google3 implementation of QuicTextUtils.
+class QuicTextUtilsImpl {
+ public:
+  // Returns true of |data| starts with |prefix|, case sensitively.
+  static bool StartsWith(base::StringPiece data, base::StringPiece prefix) {
+    return base::StartsWith(data, prefix, base::CompareCase::SENSITIVE);
+  }
+
+  // Returns true of |data| ends with |suffix|, case insensitively.
+  static bool EndsWithIgnoreCase(base::StringPiece data,
+                                 base::StringPiece suffix) {
+    return base::EndsWith(data, suffix, base::CompareCase::INSENSITIVE_ASCII);
+  }
+
+  // Returns a new std::string in which |data| has been converted to lower case.
+  static std::string ToLower(base::StringPiece data) {
+    return base::ToLowerASCII(data);
+  }
+
+  // Remove leading and trailing whitespace from |data|.
+  static void RemoveLeadingAndTrailingWhitespace(base::StringPiece* data) {
+    *data = base::TrimWhitespaceASCII(*data, base::TRIM_ALL);
+  }
+
+  // Returns true if |in| represents a valid uint64, and stores that value in
+  // |out|.
+  static bool StringToUint64(base::StringPiece in, uint64_t* out) {
+    return base::StringToUint64(in, out);
+  }
+
+  // Returns a new std::string representing |in|.
+  static std::string Uint64ToString(uint64_t in) {
+    return base::Uint64ToString(in);
+  }
+
+  // This converts |length| bytes of binary to a 2*|length|-character
+  // hexadecimal representation.
+  // Return value: 2*|length| characters of ASCII std::string.
+  static std::string HexEncode(base::StringPiece data) {
+    return base::ToLowerASCII(::base::HexEncode(data.data(), data.size()));
+  }
+
+  // Converts |data| from a hexadecimal ASCII string to a binary string
+  // that is |data.length()/2| bytes long.
+  static std::string HexDecode(base::StringPiece data) {
+    if (data.empty())
+      return "";
+    std::vector<uint8_t> v;
+    if (!base::HexStringToBytes(data.as_string(), &v))
+      return "";
+    std::string out;
+    if (!v.empty())
+      out.assign(reinterpret_cast<const char*>(&v[0]), v.size());
+    return out;
+  }
+
+  // Base64 encodes with no padding |data_len| bytes of |data| into |output|.
+  static void Base64Encode(const uint8_t* data,
+                           size_t data_len,
+                           std::string* output) {
+    base::Base64Encode(
+        std::string(reinterpret_cast<const char*>(data), data_len), output);
+    // Remove padding.
+    size_t len = output->size();
+    if (len >= 2) {
+      if ((*output)[len - 1] == '=') {
+        len--;
+        if ((*output)[len - 1] == '=') {
+          len--;
+        }
+        output->resize(len);
+      }
+    }
+  }
+
+  // Returns a std::string containing hex and ASCII representations of |binary|,
+  // side-by-side in the style of hexdump. Non-printable characters will be
+  // printed as '.' in the ASCII output.
+  // For example, given the input "Hello, QUIC!\01\02\03\04", returns:
+  // "0x0000:  4865 6c6c 6f2c 2051 5549 4321 0102 0304  Hello,.QUIC!...."
+  static std::string HexDump(base::StringPiece binary_input) {
+    int offset = 0;
+    const int kBytesPerLine = 16;  // Max bytes dumped per line
+    const char* buf = binary_input.data();
+    int bytes_remaining = binary_input.size();
+    std::string s;  // our output
+    const char* p = buf;
+    while (bytes_remaining > 0) {
+      const int line_bytes = std::min(bytes_remaining, kBytesPerLine);
+      base::StringAppendF(&s, "0x%04x:  ", offset);  // Do the line header
+      for (int i = 0; i < kBytesPerLine; ++i) {
+        if (i < line_bytes) {
+          base::StringAppendF(&s, "%02x", static_cast<unsigned char>(p[i]));
+        } else {
+          s += "  ";  // two-space filler instead of two-space hex digits
+        }
+        if (i % 2)
+          s += ' ';
+      }
+      s += ' ';
+      for (int i = 0; i < line_bytes; ++i) {  // Do the ASCII dump
+        s += (p[i] > 32 && p[i] < 127) ? p[i] : '.';
+      }
+
+      bytes_remaining -= line_bytes;
+      offset += line_bytes;
+      p += line_bytes;
+      s += '\n';
+    }
+    return s;
+  }
+
+  // Returns true if |data| contains any uppercase characters.
+  static bool ContainsUpperCase(base::StringPiece data) {
+    return std::any_of(data.begin(), data.end(), base::IsAsciiUpper<char>);
+  }
+
+  // Splits |data| into a vector of pieces delimited by |delim|.
+  static std::vector<base::StringPiece> Split(base::StringPiece data,
+                                              char delim) {
+    return base::SplitStringPiece(data, base::StringPiece(&delim, 1),
+                                  base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  }
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_PLATFORM_IMPL_QUIC_TEXT_UTILS_IMPL_H_
diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc
index fdba134..579d87b 100644
--- a/net/quic/test_tools/crypto_test_utils.cc
+++ b/net/quic/test_tools/crypto_test_utils.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "base/strings/string_util.h"
 #include "crypto/openssl_util.h"
 #include "crypto/secure_hash.h"
 #include "net/quic/core/crypto/channel_id.h"
@@ -24,6 +23,7 @@
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/platform/api/quic_clock.h"
 #include "net/quic/platform/api/quic_socket_address.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
 #include "net/quic/test_tools/quic_framer_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
@@ -992,13 +992,13 @@
       StringPiece(reinterpret_cast<const char*>(orbit.data()),
                   sizeof(orbit.size())),
       &nonce);
-  return ("#" + net::QuicUtils::HexEncode(nonce));
+  return ("#" + QuicTextUtils::HexEncode(nonce));
 }
 
 string CryptoTestUtils::GenerateClientPublicValuesHex() {
   char public_value[32];
   memset(public_value, 42, sizeof(public_value));
-  return ("#" + net::QuicUtils::HexEncode(public_value, sizeof(public_value)));
+  return ("#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value)));
 }
 
 // static
diff --git a/net/quic/test_tools/crypto_test_utils_test.cc b/net/quic/test_tools/crypto_test_utils_test.cc
index 03385567..85069446 100644
--- a/net/quic/test_tools/crypto_test_utils_test.cc
+++ b/net/quic/test_tools/crypto_test_utils_test.cc
@@ -6,6 +6,7 @@
 
 #include "net/quic/core/crypto/crypto_server_config_protobuf.h"
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/mock_clock.h"
 #include "net/test/gtest_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -91,8 +92,8 @@
 
   void ProcessClientHelloDone(std::unique_ptr<CryptoHandshakeMessage> message) {
     // Verify output is a SHLO.
-    EXPECT_EQ(message->tag(), kSHLO) << "Fail to pass validation. Get "
-                                     << message->DebugString();
+    EXPECT_EQ(message->tag(), kSHLO)
+        << "Fail to pass validation. Get " << message->DebugString();
   }
 
   QuicCryptoServerConfig* crypto_config_;
@@ -139,12 +140,12 @@
       StringPiece(reinterpret_cast<const char*>(orbit.data()),
                   sizeof(orbit.size())),
       &nonce);
-  string nonce_hex = "#" + QuicUtils::HexEncode(nonce);
+  string nonce_hex = "#" + QuicTextUtils::HexEncode(nonce);
 
   char public_value[32];
   memset(public_value, 42, sizeof(public_value));
   string pub_hex =
-      "#" + QuicUtils::HexEncode(public_value, sizeof(public_value));
+      "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
 
   QuicVersion version(AllSupportedVersions().front());
   // clang-format off
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index c0bb8914..99aefc7 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/crypto/crypto_framer.h"
 #include "net/quic/core/crypto/crypto_handshake.h"
 #include "net/quic/core/crypto/crypto_utils.h"
diff --git a/net/tools/quic/chlo_extractor.cc b/net/tools/quic/chlo_extractor.cc
index 3e5ce78..14ac9e3 100644
--- a/net/tools/quic/chlo_extractor.cc
+++ b/net/tools/quic/chlo_extractor.cc
@@ -4,13 +4,13 @@
 
 #include "net/tools/quic/chlo_extractor.h"
 
-#include "base/strings/string_util.h"
 #include "net/quic/core/crypto/crypto_framer.h"
 #include "net/quic/core/crypto/crypto_handshake_message.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
 #include "net/quic/core/crypto/quic_decrypter.h"
 #include "net/quic/core/crypto/quic_encrypter.h"
 #include "net/quic/core/quic_framer.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::StringPiece;
 
@@ -93,7 +93,7 @@
 bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
   StringPiece data(frame.data_buffer, frame.data_length);
   if (frame.stream_id == kCryptoStreamId && frame.offset == 0 &&
-      base::StartsWith(data, "CHLO", base::CompareCase::INSENSITIVE_ASCII)) {
+      QuicTextUtils::StartsWith(data, "CHLO")) {
     CryptoFramer crypto_framer;
     crypto_framer.set_visitor(this);
     if (!crypto_framer.ProcessInput(data)) {
diff --git a/net/tools/quic/crypto_message_printer_bin.cc b/net/tools/quic/crypto_message_printer_bin.cc
index 11eb8f8..d0b46c60 100644
--- a/net/tools/quic/crypto_message_printer_bin.cc
+++ b/net/tools/quic/crypto_message_printer_bin.cc
@@ -11,7 +11,7 @@
 
 #include "base/command_line.h"
 #include "net/quic/core/crypto/crypto_framer.h"
-#include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using std::cerr;
 using std::cout;
@@ -44,7 +44,7 @@
   net::CryptoMessagePrinter printer;
   net::CryptoFramer framer;
   framer.set_visitor(&printer);
-  std::string input = net::QuicUtils::HexDecode(argv[1]);
+  std::string input = net::QuicTextUtils::HexDecode(argv[1]);
   if (!framer.ProcessInput(input)) {
     return 1;
   }
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index e8dc19c..c2ca6dad 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -15,7 +15,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
@@ -33,6 +32,7 @@
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/platform/api/quic_socket_address.h"
 #include "net/quic/platform/api/quic_str_cat.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/quic_config_peer.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
@@ -2254,7 +2254,8 @@
     DVLOG(1) << "Sending error response for stream " << id();
     SpdyHeaderBlock headers;
     headers[":status"] = "500";
-    headers["content-length"] = base::UintToString(response_body_.size());
+    headers["content-length"] =
+        QuicTextUtils::Uint64ToString(response_body_.size());
     // This method must call CloseReadSide to cause the test case, StopReading
     // is not sufficient.
     QuicStreamPeer::CloseReadSide(this);
@@ -2587,7 +2588,7 @@
   SpdyHeaderBlock headers;
   headers[":status"] = "200";
   headers[":version"] = "HTTP/1.1";
-  headers["content-length"] = IntToString(kBody.size());
+  headers["content-length"] = QuicTextUtils::Uint64ToString(kBody.size());
 
   SpdyHeaderBlock trailers;
   trailers["some-trailing-header"] = "trailing-header-value";
@@ -2641,7 +2642,8 @@
       SpdyHeaderBlock response_headers;
       response_headers[":version"] = "HTTP/1.1";
       response_headers[":status"] = "200";
-      response_headers["content-length"] = IntToString(body.size());
+      response_headers["content-length"] =
+          QuicTextUtils::Uint64ToString(body.size());
       push_resources.push_back(QuicHttpResponseCache::ServerPushInfo(
           resource_url, std::move(response_headers), kV3LowestPriority, body));
     }
@@ -2943,7 +2945,8 @@
   headers[":path"] = "/foo";
   headers[":scheme"] = "https";
   headers[":authority"] = server_hostname_;
-  headers["content-length"] = IntToString(request_body_size_bytes);
+  headers["content-length"] =
+      QuicTextUtils::Uint64ToString(request_body_size_bytes);
 
   client_->SendMessage(headers, "", /*fin=*/false);
 
diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc
index a7e04b02..c074146 100644
--- a/net/tools/quic/quic_client.cc
+++ b/net/tools/quic/quic_client.cc
@@ -13,7 +13,6 @@
 
 #include "base/logging.h"
 #include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/base/sockaddr_storage.h"
 #include "net/quic/core/crypto/quic_random.h"
 #include "net/quic/core/quic_bug_tracker.h"
@@ -34,7 +33,6 @@
 // TODO(rtenneti): Add support for MMSG_MORE.
 #define MMSG_MORE 0
 using base::StringPiece;
-using base::StringToInt;
 using std::string;
 
 namespace net {
diff --git a/net/tools/quic/quic_client_base.cc b/net/tools/quic/quic_client_base.cc
index d6b30d8..b1ada21d 100644
--- a/net/tools/quic/quic_client_base.cc
+++ b/net/tools/quic/quic_client_base.cc
@@ -4,10 +4,10 @@
 
 #include "net/tools/quic/quic_client_base.h"
 
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/crypto/quic_random.h"
 #include "net/quic/core/quic_server_id.h"
 #include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using base::StringPiece;
 using base::StringToInt;
diff --git a/net/tools/quic/quic_client_bin.cc b/net/tools/quic/quic_client_bin.cc
index 1afe04c..2f4ac4d8 100644
--- a/net/tools/quic/quic_client_bin.cc
+++ b/net/tools/quic/quic_client_bin.cc
@@ -44,10 +44,6 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
 #include "net/base/net_errors.h"
 #include "net/base/privacy_mode.h"
 #include "net/cert/cert_verifier.h"
@@ -60,6 +56,7 @@
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/platform/api/quic_socket_address.h"
 #include "net/quic/platform/api/quic_str_cat.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/spdy/spdy_header_block.h"
 #include "net/tools/epoll_server/epoll_server.h"
 #include "net/tools/quic/quic_client.h"
@@ -75,6 +72,7 @@
 using net::ProofVerifierChromium;
 using net::SpdyHeaderBlock;
 using net::TransportSecurityState;
+using net::QuicTextUtils;
 using std::cout;
 using std::cerr;
 using std::string;
@@ -299,7 +297,7 @@
   string body = FLAGS_body;
   if (!FLAGS_body_hex.empty()) {
     DCHECK(FLAGS_body.empty()) << "Only set one of --body and --body_hex.";
-    body = net::QuicUtils::HexDecode(FLAGS_body_hex);
+    body = QuicTextUtils::HexDecode(FLAGS_body_hex);
   }
 
   // Construct a GET or POST request for supplied URL.
@@ -310,21 +308,14 @@
   header_block[":path"] = url.path();
 
   // Append any additional headers supplied on the command line.
-  for (const std::string& header :
-       base::SplitString(FLAGS_headers, ";", base::KEEP_WHITESPACE,
-                         base::SPLIT_WANT_NONEMPTY)) {
-    string sp;
-    base::TrimWhitespaceASCII(header, base::TRIM_ALL, &sp);
+  for (StringPiece sp : QuicTextUtils::Split(FLAGS_headers, ';')) {
+    QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&sp);
     if (sp.empty()) {
       continue;
     }
-    std::vector<string> kv =
-        base::SplitString(sp, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-    CHECK_EQ(2u, kv.size());
-    string key;
-    base::TrimWhitespaceASCII(kv[0], base::TRIM_ALL, &key);
-    string value;
-    base::TrimWhitespaceASCII(kv[1], base::TRIM_ALL, &value);
+    std::vector<StringPiece> kv = QuicTextUtils::Split(sp, ':');
+    QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[0]);
+    QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[1]);
     header_block[kv[0]] = kv[1];
   }
 
@@ -340,7 +331,7 @@
     if (!FLAGS_body_hex.empty()) {
       // Print the user provided hex, rather than binary body.
       cout << "body:\n"
-           << net::QuicUtils::HexDump(net::QuicUtils::HexDecode(FLAGS_body_hex))
+           << QuicTextUtils::HexDump(QuicTextUtils::HexDecode(FLAGS_body_hex))
            << endl;
     } else {
       cout << "body: " << body << endl;
@@ -351,7 +342,7 @@
     string response_body = client.latest_response_body();
     if (!FLAGS_body_hex.empty()) {
       // Assume response is binary data.
-      cout << "body:\n" << net::QuicUtils::HexDump(response_body) << endl;
+      cout << "body:\n" << QuicTextUtils::HexDump(response_body) << endl;
     } else {
       cout << "body: " << response_body << endl;
     }
diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc
index 4e9b68e..7f4304f79 100644
--- a/net/tools/quic/quic_client_session_test.cc
+++ b/net/tools/quic/quic_client_session_test.cc
@@ -22,8 +22,8 @@
 #include "net/tools/quic/quic_spdy_client_stream.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::StringPrintf;
 using google::protobuf::implicit_cast;
+using base::StringPrintf;
 using std::string;
 using testing::AnyNumber;
 using testing::Invoke;
diff --git a/net/tools/quic/quic_client_test.cc b/net/tools/quic/quic_client_test.cc
index 3ce369af..1037aedc 100644
--- a/net/tools/quic/quic_client_test.cc
+++ b/net/tools/quic/quic_client_test.cc
@@ -11,7 +11,7 @@
 
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
-#include "base/strings/string_util.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "net/tools/epoll_server/epoll_server.h"
@@ -37,8 +37,7 @@
     if (!base::ReadSymbolicLink(entry, &fd_path)) {
       continue;
     }
-    if (base::StartsWith(fd_path.value(), "socket:",
-                         base::CompareCase::SENSITIVE)) {
+    if (QuicTextUtils::StartsWith(fd_path.value(), "socket:")) {
       socket_count++;
     }
   }
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc
index cf6723c4..87382d9 100644
--- a/net/tools/quic/quic_dispatcher_test.cc
+++ b/net/tools/quic/quic_dispatcher_test.cc
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/crypto/crypto_handshake.h"
 #include "net/quic/core/crypto/quic_crypto_server_config.h"
 #include "net/quic/core/crypto/quic_random.h"
@@ -38,7 +37,6 @@
 #include "testing/gmock_mutant.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::IntToString;
 using base::StringPiece;
 using std::string;
 using testing::CreateFunctor;
diff --git a/net/tools/quic/quic_http_response_cache.cc b/net/tools/quic/quic_http_response_cache.cc
index bc6c819..c67ece77 100644
--- a/net/tools/quic/quic_http_response_cache.cc
+++ b/net/tools/quic/quic_http_response_cache.cc
@@ -10,11 +10,9 @@
 #include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
 #include "net/http/http_util.h"
 #include "net/quic/core/quic_bug_tracker.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/spdy/spdy_http_utils.h"
 
 using base::FilePath;
@@ -88,7 +86,7 @@
       return;
     }
     spdy_headers_.AppendValueOrAddHeader(
-        base::ToLowerASCII(line.substr(0, pos)), line.substr(pos + 2));
+        QuicTextUtils::ToLower(line.substr(0, pos)), line.substr(pos + 2));
   }
 
   // The connection header is prohibited in HTTP/2.
@@ -144,10 +142,9 @@
 }
 
 StringPiece QuicHttpResponseCache::ResourceFile::RemoveScheme(StringPiece url) {
-  if (base::StartsWith(url, "https://", base::CompareCase::INSENSITIVE_ASCII)) {
+  if (QuicTextUtils::StartsWith(url, "https://")) {
     url.remove_prefix(8);
-  } else if (base::StartsWith(url, "http://",
-                              base::CompareCase::INSENSITIVE_ASCII)) {
+  } else if (QuicTextUtils::StartsWith(url, "http://")) {
     url.remove_prefix(7);
   }
   return url;
@@ -184,9 +181,9 @@
                                               int response_code,
                                               StringPiece body) {
   SpdyHeaderBlock response_headers;
-  response_headers[":status"] = IntToString(response_code);
+  response_headers[":status"] = QuicTextUtils::Uint64ToString(response_code);
   response_headers["content-length"] =
-      IntToString(static_cast<int>(body.length()));
+      QuicTextUtils::Uint64ToString(body.length());
   AddResponse(host, path, std::move(response_headers), body);
 }
 
diff --git a/net/tools/quic/quic_http_response_cache_test.cc b/net/tools/quic/quic_http_response_cache_test.cc
index 6cd73120..fa2fce6 100644
--- a/net/tools/quic/quic_http_response_cache_test.cc
+++ b/net/tools/quic/quic_http_response_cache_test.cc
@@ -8,15 +8,13 @@
 #include "base/memory/singleton.h"
 #include "base/path_service.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
 #include "net/quic/platform/api/quic_str_cat.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/spdy/spdy_framer.h"
 #include "net/tools/quic/quic_http_response_cache.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::ContainsKey;
-using base::IntToString;
 using base::StringPiece;
 using net::SpdyHeaderBlock;
 using std::string;
@@ -78,7 +76,8 @@
   SpdyHeaderBlock response_headers;
   response_headers[":version"] = "HTTP/1.1";
   response_headers[":status"] = "200";
-  response_headers["content-length"] = IntToString(kResponseBody.size());
+  response_headers["content-length"] =
+      QuicTextUtils::Uint64ToString(kResponseBody.size());
 
   SpdyHeaderBlock response_trailers;
   response_trailers["key-1"] = "value-1";
@@ -177,14 +176,15 @@
   std::list<QuicHttpResponseCache::ServerPushInfo> push_resources;
   string scheme = "http";
   for (int i = 0; i < NumResources; ++i) {
-    string path = "/server_push_src" + base::IntToString(i);
+    string path = "/server_push_src" + QuicTextUtils::Uint64ToString(i);
     string url = scheme + "://" + request_host + path;
     GURL resource_url(url);
     string body = QuicStrCat("This is server push response body for ", path);
     SpdyHeaderBlock response_headers;
     response_headers[":version"] = "HTTP/1.1";
     response_headers[":status"] = "200";
-    response_headers["content-length"] = base::UintToString(body.size());
+    response_headers["content-length"] =
+        QuicTextUtils::Uint64ToString(body.size());
     push_resources.push_back(
         ServerPushInfo(resource_url, response_headers.Clone(), i, body));
   }
@@ -212,14 +212,15 @@
   string push_response_status[kNumResources] = {"200", "200", "301", "404"};
   std::list<QuicHttpResponseCache::ServerPushInfo> push_resources;
   for (int i = 0; i < NumResources; ++i) {
-    string path = "/server_push_src" + base::IntToString(i);
+    string path = "/server_push_src" + QuicTextUtils::Uint64ToString(i);
     string url = scheme + "://" + request_host + path;
     GURL resource_url(url);
     string body = "This is server push response body for " + path;
     SpdyHeaderBlock response_headers;
     response_headers[":version"] = "HTTP/1.1";
     response_headers[":status"] = push_response_status[i];
-    response_headers["content-length"] = base::UintToString(body.size());
+    response_headers["content-length"] =
+        QuicTextUtils::Uint64ToString(body.size());
     push_resources.push_back(
         ServerPushInfo(resource_url, response_headers.Clone(), i, body));
   }
diff --git a/net/tools/quic/quic_packet_printer_bin.cc b/net/tools/quic/quic_packet_printer_bin.cc
index 1ce089b..2ba19b9 100644
--- a/net/tools/quic/quic_packet_printer_bin.cc
+++ b/net/tools/quic/quic_packet_printer_bin.cc
@@ -36,10 +36,10 @@
 #include <string>
 
 #include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "net/quic/core/quic_framer.h"
 #include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 
 using std::cerr;
 using std::string;
@@ -103,7 +103,7 @@
   bool OnStreamFrame(const QuicStreamFrame& frame) override {
     cerr << "OnStreamFrame: " << frame;
     cerr << "         data: { "
-         << QuicUtils::HexEncode(frame.data_buffer, frame.data_length)
+         << QuicTextUtils::HexEncode(frame.data_buffer, frame.data_length)
          << " }\n";
     return true;
   }
@@ -181,7 +181,7 @@
          << " Usage: " << args[0] << " client|server <hex>\n";
     return 1;
   }
-  string hex = net::QuicUtils::HexDecode(ArgToString(args[1]));
+  string hex = net::QuicTextUtils::HexDecode(argv[2]);
   net::QuicVersionVector versions = net::AllSupportedVersions();
   // Fake a time since we're not actually generating acks.
   net::QuicTime start(net::QuicTime::Zero());
diff --git a/net/tools/quic/quic_simple_client_bin.cc b/net/tools/quic/quic_simple_client_bin.cc
index fc11a7f..560d607b 100644
--- a/net/tools/quic/quic_simple_client_bin.cc
+++ b/net/tools/quic/quic_simple_client_bin.cc
@@ -59,7 +59,7 @@
 #include "net/quic/core/quic_error_codes.h"
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/core/quic_server_id.h"
-#include "net/quic/core/quic_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/spdy/spdy_header_block.h"
 #include "net/spdy/spdy_http_utils.h"
 #include "net/tools/quic/quic_simple_client.h"
@@ -73,6 +73,7 @@
 using net::MultiLogCTVerifier;
 using net::ProofVerifier;
 using net::ProofVerifierChromium;
+using net::QuicTextUtils;
 using net::TransportSecurityState;
 using std::cout;
 using std::cerr;
@@ -297,7 +298,7 @@
   string body = FLAGS_body;
   if (!FLAGS_body_hex.empty()) {
     DCHECK(FLAGS_body.empty()) << "Only set one of --body and --body_hex.";
-    body = net::QuicUtils::HexDecode(FLAGS_body_hex);
+    body = QuicTextUtils::HexDecode(FLAGS_body_hex);
   }
 
   // Construct a GET or POST request for supplied URL.
@@ -340,7 +341,7 @@
     if (!FLAGS_body_hex.empty()) {
       // Print the user provided hex, rather than binary body.
       cout << "body:\n"
-           << net::QuicUtils::HexDump(net::QuicUtils::HexDecode(FLAGS_body_hex))
+           << QuicTextUtils::HexDump(QuicTextUtils::HexDecode(FLAGS_body_hex))
            << endl;
     } else {
       cout << "body: " << body << endl;
@@ -351,7 +352,7 @@
     string response_body = client.latest_response_body();
     if (!FLAGS_body_hex.empty()) {
       // Assume response is binary data.
-      cout << "body:\n" << net::QuicUtils::HexDump(response_body) << endl;
+      cout << "body:\n" << QuicTextUtils::HexDump(response_body) << endl;
     } else {
       cout << "body: " << response_body << endl;
     }
diff --git a/net/tools/quic/quic_simple_server_session_test.cc b/net/tools/quic/quic_simple_server_session_test.cc
index 125cb78..d82973a 100644
--- a/net/tools/quic/quic_simple_server_session_test.cc
+++ b/net/tools/quic/quic_simple_server_session_test.cc
@@ -16,6 +16,7 @@
 #include "net/quic/core/quic_crypto_server_stream.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/platform/api/quic_socket_address.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/quic_config_peer.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
@@ -462,7 +463,8 @@
     string scheme = "http";
     for (unsigned int i = 1; i <= num_resources; ++i) {
       QuicStreamId stream_id = i * 2;
-      string path = partial_push_resource_path + base::UintToString(i);
+      string path =
+          partial_push_resource_path + QuicTextUtils::Uint64ToString(i);
       string url = scheme + "://" + resource_host + path;
       GURL resource_url = GURL(url);
       string body(body_size, 'a');
diff --git a/net/tools/quic/quic_simple_server_stream.cc b/net/tools/quic/quic_simple_server_stream.cc
index 89f262e..5c437d6 100644
--- a/net/tools/quic/quic_simple_server_stream.cc
+++ b/net/tools/quic/quic_simple_server_stream.cc
@@ -9,19 +9,16 @@
 
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_spdy_stream.h"
 #include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/spdy/spdy_protocol.h"
 #include "net/tools/quic/quic_http_response_cache.h"
 #include "net/tools/quic/quic_simple_server_session.h"
 
 using base::StringPiece;
-using base::StringToInt;
 using std::string;
 
 namespace net {
@@ -205,7 +202,8 @@
   DVLOG(1) << "Sending not found response for stream " << id();
   SpdyHeaderBlock headers;
   headers[":status"] = "404";
-  headers["content-length"] = base::IntToString(strlen(kNotFoundResponseBody));
+  headers["content-length"] =
+      QuicTextUtils::Uint64ToString(strlen(kNotFoundResponseBody));
   SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
 }
 
@@ -213,7 +211,8 @@
   DVLOG(1) << "Sending error response for stream " << id();
   SpdyHeaderBlock headers;
   headers[":status"] = "500";
-  headers["content-length"] = base::UintToString(strlen(kErrorResponseBody));
+  headers["content-length"] =
+      QuicTextUtils::Uint64ToString(strlen(kErrorResponseBody));
   SendHeadersAndBody(std::move(headers), kErrorResponseBody);
 }
 
diff --git a/net/tools/quic/quic_spdy_client_stream.cc b/net/tools/quic/quic_spdy_client_stream.cc
index 2137a6b5..f54b6a46 100644
--- a/net/tools/quic/quic_spdy_client_stream.cc
+++ b/net/tools/quic/quic_spdy_client_stream.cc
@@ -8,7 +8,6 @@
 
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/quic_alarm.h"
 #include "net/quic/core/quic_client_promised_info.h"
 #include "net/quic/core/spdy_utils.h"
@@ -17,7 +16,6 @@
 
 using base::StringPiece;
 using std::string;
-using base::StringToInt;
 
 namespace net {
 
diff --git a/net/tools/quic/quic_spdy_client_stream_test.cc b/net/tools/quic/quic_spdy_client_stream_test.cc
index 055e728..b9d50ee 100644
--- a/net/tools/quic/quic_spdy_client_stream_test.cc
+++ b/net/tools/quic/quic_spdy_client_stream_test.cc
@@ -7,10 +7,10 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/core/spdy_utils.h"
 #include "net/quic/platform/api/quic_socket_address.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "net/tools/quic/quic_client_session.h"
@@ -187,7 +187,8 @@
   // promised by the final offset field.
   SpdyHeaderBlock trailer_block;
   trailer_block["trailer key"] = "trailer value";
-  trailer_block[kFinalOffsetHeaderKey] = IntToString(body_.size());
+  trailer_block[kFinalOffsetHeaderKey] =
+      QuicTextUtils::Uint64ToString(body_.size());
   auto trailers = AsHeaderList(trailer_block);
   stream_->OnStreamHeaderList(true, trailers.uncompressed_header_bytes(),
                               trailers);
diff --git a/net/tools/quic/stateless_rejector_test.cc b/net/tools/quic/stateless_rejector_test.cc
index 9ecef23..c2ab54c4 100644
--- a/net/tools/quic/stateless_rejector_test.cc
+++ b/net/tools/quic/stateless_rejector_test.cc
@@ -8,11 +8,11 @@
 #include <vector>
 
 #include "base/memory/ptr_util.h"
-#include "base/strings/stringprintf.h"
 #include "net/quic/core/crypto/crypto_handshake_message.h"
 #include "net/quic/core/crypto/proof_source.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/platform/api/quic_str_cat.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/quic_crypto_server_config_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
@@ -100,7 +100,8 @@
         QuicRandom::GetInstance(), &clock_, config_options_));
 
     // Save the server config.
-    scid_hex_ = "#" + QuicUtils::HexEncode(config_peer_.GetPrimaryConfig()->id);
+    scid_hex_ =
+        "#" + QuicTextUtils::HexEncode(config_peer_.GetPrimaryConfig()->id);
 
     // Encode the QUIC version.
     ver_hex_ = QuicTagToString(QuicVersionToQuicTag(GetParam().version));
@@ -108,7 +109,8 @@
     // Generate a public value.
     char public_value[32];
     memset(public_value, 42, sizeof(public_value));
-    pubs_hex_ = "#" + QuicUtils::HexEncode(public_value, sizeof(public_value));
+    pubs_hex_ =
+        "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
 
     // Generate a client nonce.
     string nonce;
@@ -118,7 +120,7 @@
             reinterpret_cast<char*>(config_peer_.GetPrimaryConfig()->orbit),
             kOrbitSize),
         &nonce);
-    nonc_hex_ = "#" + QuicUtils::HexEncode(nonce);
+    nonc_hex_ = "#" + QuicTextUtils::HexEncode(nonce);
 
     // Generate a source address token.
     SourceAddressTokens previous_tokens;
@@ -127,7 +129,7 @@
     string stk = config_peer_.NewSourceAddressToken(
         config_peer_.GetPrimaryConfig()->id, previous_tokens, ip, &rand,
         clock_.WallNow(), nullptr);
-    stk_hex_ = "#" + QuicUtils::HexEncode(stk);
+    stk_hex_ = "#" + QuicTextUtils::HexEncode(stk);
   }
 
  protected:
@@ -253,8 +255,8 @@
 TEST_P(StatelessRejectorTest, AcceptChlo) {
   const uint64_t xlct = CryptoTestUtils::LeafCertHashForTesting();
   const string xlct_hex =
-      "#" +
-      QuicUtils::HexEncode(reinterpret_cast<const char*>(&xlct), sizeof(xlct));
+      "#" + QuicTextUtils::HexEncode(reinterpret_cast<const char*>(&xlct),
+                                     sizeof(xlct));
   // clang-format off
   const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message(
       "CHLO",
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index fea5045..59e8e31 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/memory/ptr_util.h"
-#include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "net/base/completion_callback.h"
 #include "net/base/net_errors.h"
@@ -20,6 +19,7 @@
 #include "net/quic/core/quic_server_id.h"
 #include "net/quic/core/quic_utils.h"
 #include "net/quic/core/spdy_utils.h"
+#include "net/quic/platform/api/quic_text_utils.h"
 #include "net/quic/test_tools/crypto_test_utils.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
 #include "net/quic/test_tools/quic_spdy_session_peer.h"
@@ -701,8 +701,8 @@
 bool QuicTestClient::PopulateHeaderBlockFromUrl(const string& uri,
                                                 SpdyHeaderBlock* headers) {
   string url;
-  if (base::StartsWith(uri, "https://", base::CompareCase::INSENSITIVE_ASCII) ||
-      base::StartsWith(uri, "http://", base::CompareCase::INSENSITIVE_ASCII)) {
+  if (QuicTextUtils::StartsWith(uri, "https://") ||
+      QuicTextUtils::StartsWith(uri, "http://")) {
     url = uri;
   } else if (uri[0] == '/') {
     url = "https://" + client_->server_id().host() + uri;
diff --git a/pdf/pdfium/DEPS b/pdf/pdfium/DEPS
index 1f87653..0fdd714 100644
--- a/pdf/pdfium/DEPS
+++ b/pdf/pdfium/DEPS
@@ -5,4 +5,5 @@
   "+printing/units.h",
   "+third_party/pdfium/public",
   "+ui/gfx/geometry/rect.h",
+  "+ui/gfx/codec/jpeg_codec.h",
 ]
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 35c2896..63f497c8 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -57,6 +57,7 @@
 #include "third_party/pdfium/public/fpdf_transformpage.h"
 #include "third_party/pdfium/public/fpdfview.h"
 #include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/gfx/geometry/rect.h"
 #include "v8/include/v8.h"
 
@@ -617,6 +618,17 @@
   g_isolate_holder = nullptr;
 }
 
+int GetBlockForJpeg(void* param,
+                    unsigned long pos,
+                    unsigned char* buf,
+                    unsigned long size) {
+  std::vector<uint8_t>* data_vector = static_cast<std::vector<uint8_t>*>(param);
+  if (pos + size < pos || pos + size > data_vector->size())
+    return 0;
+  memcpy(buf, data_vector->data() + pos, size);
+  return 1;
+}
+
 }  // namespace
 
 bool InitializeSDK() {
@@ -1353,9 +1365,11 @@
 }
 
 uint32_t PDFiumEngine::QuerySupportedPrintOutputFormats() {
-  if (!HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY))
-    return 0;
-  return PP_PRINTOUTPUTFORMAT_PDF;
+  if (HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY))
+    return PP_PRINTOUTPUTFORMAT_PDF | PP_PRINTOUTPUTFORMAT_RASTER;
+  if (HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY))
+    return PP_PRINTOUTPUTFORMAT_RASTER;
+  return 0;
 }
 
 void PDFiumEngine::PrintBegin() {
@@ -1366,10 +1380,13 @@
     const PP_PrintPageNumberRange_Dev* page_ranges, uint32_t page_range_count,
     const PP_PrintSettings_Dev& print_settings) {
   ScopedSubstFont scoped_subst_font(this);
-  if (HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY))
+  if (HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY) &&
+      (print_settings.format & PP_PRINTOUTPUTFORMAT_PDF)) {
     return PrintPagesAsPDF(page_ranges, page_range_count, print_settings);
-  else if (HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY))
+  } else if (HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY)) {
     return PrintPagesAsRasterPDF(page_ranges, page_range_count, print_settings);
+  }
+
   return pp::Resource();
 }
 
@@ -1412,6 +1429,8 @@
                         print_settings.orientation,
                         FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
 
+  unsigned char* bitmap_data =
+      static_cast<unsigned char*>(FPDFBitmap_GetBuffer(bitmap));
   double ratio_x = ConvertUnitDouble(bitmap_size.width(),
                                      print_settings.dpi,
                                      kPointsPerInch);
@@ -1422,7 +1441,25 @@
   // Add the bitmap to an image object and add the image object to the output
   // page.
   FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImgeObj(temp_doc);
-  FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, bitmap);
+
+  std::vector<uint8_t> compressed_bitmap_data;
+  int quality = 40;
+  if (!(print_settings.format & PP_PRINTOUTPUTFORMAT_PDF) &&
+      (gfx::JPEGCodec::Encode(
+          bitmap_data, gfx::JPEGCodec::FORMAT_BGRA, FPDFBitmap_GetWidth(bitmap),
+          FPDFBitmap_GetHeight(bitmap), FPDFBitmap_GetStride(bitmap), quality,
+          &compressed_bitmap_data))) {
+    FPDF_FILEACCESS file_access = {};
+    file_access.m_FileLen =
+        static_cast<unsigned long>(compressed_bitmap_data.size());
+    file_access.m_GetBlock = &GetBlockForJpeg;
+    file_access.m_Param = &compressed_bitmap_data;
+
+    FPDFImageObj_LoadJpegFileInline(&temp_page, 1, temp_img, &file_access);
+  } else {
+    FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, bitmap);
+  }
+
   FPDFImageObj_SetMatrix(temp_img, ratio_x, 0, 0, ratio_y, 0, 0);
   FPDFPage_InsertObject(temp_page, temp_img);
   FPDFPage_GenerateContent(temp_page);
diff --git a/printing/print_job_constants.cc b/printing/print_job_constants.cc
index 6dad017c..b626e93d 100644
--- a/printing/print_job_constants.cc
+++ b/printing/print_job_constants.cc
@@ -172,6 +172,9 @@
 // Scaling factor
 const char kSettingScaleFactor[] = "scaleFactor";
 
+// Scaling factor
+const char kSettingRasterizePdf[] = "rasterizePDF";
+
 // Ticket option. Contains the ticket in CJT format.
 const char kSettingTicket[] = "ticket";
 
diff --git a/printing/print_job_constants.h b/printing/print_job_constants.h
index 7118808a..1fffdd4a 100644
--- a/printing/print_job_constants.h
+++ b/printing/print_job_constants.h
@@ -65,6 +65,7 @@
 PRINTING_EXPORT extern const char kSettingPrinterDescription[];
 PRINTING_EXPORT extern const char kSettingPrinterName[];
 PRINTING_EXPORT extern const char kSettingPrinterOptions[];
+PRINTING_EXPORT extern const char kSettingRasterizePdf[];
 PRINTING_EXPORT extern const char kSettingScaleFactor[];
 PRINTING_EXPORT extern const char kSettingTicket[];
 PRINTING_EXPORT extern const char kSettingShouldPrintBackgrounds[];
diff --git a/printing/print_settings.h b/printing/print_settings.h
index 301bff5..67ad091c 100644
--- a/printing/print_settings.h
+++ b/printing/print_settings.h
@@ -96,6 +96,9 @@
   void set_scale_factor(double scale_factor) { scale_factor_ = scale_factor; }
   double scale_factor() const { return scale_factor_; }
 
+  void set_rasterize_pdf(bool rasterize_pdf) { rasterize_pdf_ = rasterize_pdf; }
+  bool rasterize_pdf() const { return rasterize_pdf_; }
+
   void set_supports_alpha_blend(bool supports_alpha_blend) {
     supports_alpha_blend_ = supports_alpha_blend;
   }
@@ -212,6 +215,9 @@
   // Scale factor
   double scale_factor_;
 
+  // True if PDF should be printed as a raster PDF
+  bool rasterize_pdf_;
+
   // Is the orientation landscape or portrait.
   bool landscape_;
 
diff --git a/printing/print_settings_conversion.cc b/printing/print_settings_conversion.cc
index 02fe34e..096cb90 100644
--- a/printing/print_settings_conversion.cc
+++ b/printing/print_settings_conversion.cc
@@ -177,6 +177,7 @@
   bool collate = false;
   int copies = 1;
   int scale_factor = 100;
+  bool rasterize_pdf = false;
 
   if (!job_settings.GetBoolean(kSettingCollate, &collate) ||
       !job_settings.GetInteger(kSettingCopies, &copies) ||
@@ -184,7 +185,8 @@
       !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
       !job_settings.GetBoolean(kSettingLandscape, &landscape) ||
       !job_settings.GetString(kSettingDeviceName, &device_name) ||
-      !job_settings.GetInteger(kSettingScaleFactor, &scale_factor)) {
+      !job_settings.GetInteger(kSettingScaleFactor, &scale_factor) ||
+      !job_settings.GetBoolean(kSettingRasterizePdf, &rasterize_pdf)) {
     return false;
   }
 
@@ -195,7 +197,7 @@
   settings->set_duplex_mode(static_cast<DuplexMode>(duplex_mode));
   settings->set_color(static_cast<ColorModel>(color));
   settings->set_scale_factor(static_cast<double>(scale_factor) / 100.0);
-
+  settings->set_rasterize_pdf(rasterize_pdf);
 #if defined(OS_WIN)
   // Modifiable implies HTML and not other formats like PDF.
   bool can_modify = false;
diff --git a/printing/printing_context.cc b/printing/printing_context.cc
index 6e69c86..e317924 100644
--- a/printing/printing_context.cc
+++ b/printing/printing_context.cc
@@ -73,6 +73,7 @@
   pdf_settings->SetBoolean(kSettingPrintWithPrivet, false);
   pdf_settings->SetBoolean(kSettingPrintWithExtension, false);
   pdf_settings->SetInteger(kSettingScaleFactor, 100);
+  pdf_settings->SetBoolean(kSettingRasterizePdf, false);
   return UpdatePrintSettings(*pdf_settings);
 }
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 6716526..4f016c92 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1595,6 +1595,24 @@
             ]
         }
     ],
+    "PrintPdfAsImage": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "PrintPdfAsImage"
+                    ]
+                }
+            ]
+        }
+    ],
     "PrintScaling": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index c24abe4..e68c6bb 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -138,6 +138,7 @@
 Bug(none) compositing/geometry/flipped-writing-mode.html [ Failure ]
 Bug(none) compositing/geometry/foreground-layer.html [ Failure ]
 Bug(none) compositing/geometry/horizontal-scroll-composited.html [ Failure ]
+Bug(none) compositing/geometry/layer-due-to-layer-children-deep.html [ Failure ]
 Bug(none) compositing/geometry/layer-due-to-layer-children-deep-switch.html [ Failure ]
 Bug(none) compositing/geometry/layer-due-to-layer-children-switch.html [ Failure ]
 Bug(none) compositing/geometry/layer-due-to-layer-children.html [ Failure ]
@@ -217,6 +218,7 @@
 Bug(none) compositing/iframes/composited-parent-iframe.html [ Failure ]
 Bug(none) compositing/iframes/connect-compositing-iframe2.html [ Failure ]
 Bug(none) compositing/iframes/connect-compositing-iframe3.html [ Failure ]
+Bug(none) compositing/iframes/fixed-position-iframe.html [ Failure ]
 Bug(none) compositing/iframes/iframe-resize.html [ Failure ]
 Bug(none) compositing/iframes/iframe-size-from-zero.html [ Failure ]
 Bug(none) compositing/iframes/invisible-nested-iframe-hide.html [ Crash Failure ]
@@ -480,6 +482,7 @@
 Bug(none) fast/block/positioning/relative-overflow-block.html [ Failure ]
 Bug(none) fast/block/positioning/relative-overflow-replaced-float.html [ Failure ]
 Bug(none) fast/block/positioning/relative-overflow-replaced.html [ Failure ]
+Bug(none) fast/block/positioning/rtl-fixed-positioning.html [ Failure ]
 Bug(none) fast/block/positioning/vertical-lr/002.html [ Failure ]
 Bug(none) fast/block/positioning/vertical-rl/002.html [ Failure ]
 Bug(none) fast/body-propagation/overflow/001-xhtml.xhtml [ Failure ]
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 593f586..8b784e21 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -730,7 +730,6 @@
   m_graphicsLayer->setHasWillChangeTransformHint(
       style.hasWillChangeTransformHint());
 
-  m_owningLayer.update3DTransformedDescendantStatus();
   if (style.preserves3D() && style.hasOpacity() &&
       m_owningLayer.has3DTransformedDescendant())
     UseCounter::count(layoutObject->document(),
diff --git a/third_party/WebKit/Source/core/layout/compositing/PaintLayerCompositor.cpp b/third_party/WebKit/Source/core/layout/compositing/PaintLayerCompositor.cpp
index e6946ab..212e75d 100644
--- a/third_party/WebKit/Source/core/layout/compositing/PaintLayerCompositor.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/PaintLayerCompositor.cpp
@@ -452,9 +452,10 @@
     m_needsUpdateFixedBackground = false;
   }
 
-  for (unsigned i = 0; i < layersNeedingPaintInvalidation.size(); i++)
+  for (unsigned i = 0; i < layersNeedingPaintInvalidation.size(); i++) {
     forceRecomputeVisualRectsIncludingNonCompositingDescendants(
         layersNeedingPaintInvalidation[i]->layoutObject());
+  }
 
   // Inform the inspector that the layer tree has changed.
   if (m_layoutView.frame()->isMainFrame())
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGContainer.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGContainer.cpp
index 14ea841..e24927b 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGContainer.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGContainer.cpp
@@ -111,18 +111,23 @@
   bool hadIsolation =
       oldStyle && !isSVGHiddenContainer() &&
       SVGLayoutSupport::willIsolateBlendingDescendantsForStyle(*oldStyle);
-  bool isolationChanged =
-      hadIsolation ==
-      !SVGLayoutSupport::willIsolateBlendingDescendantsForObject(this);
+
+  bool willIsolateBlendingDescendants =
+      SVGLayoutSupport::willIsolateBlendingDescendantsForObject(this);
+
+  bool isolationChanged = hadIsolation != willIsolateBlendingDescendants;
+
+  if (isolationChanged)
+    setNeedsPaintPropertyUpdate();
 
   if (!parent() || !isolationChanged)
     return;
 
-  if (hasNonIsolatedBlendingDescendants())
+  if (hasNonIsolatedBlendingDescendants()) {
     parent()->descendantIsolationRequirementsChanged(
-        SVGLayoutSupport::willIsolateBlendingDescendantsForObject(this)
-            ? DescendantIsolationNeedsUpdate
-            : DescendantIsolationRequired);
+        willIsolateBlendingDescendants ? DescendantIsolationNeedsUpdate
+                                       : DescendantIsolationRequired);
+  }
 }
 
 bool LayoutSVGContainer::hasNonIsolatedBlendingDescendants() const {
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGModelObject.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGModelObject.cpp
index 98329bd..7e3c825 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGModelObject.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGModelObject.cpp
@@ -125,6 +125,9 @@
       parent()->descendantIsolationRequirementsChanged(
           style()->hasBlendMode() ? DescendantIsolationRequired
                                   : DescendantIsolationNeedsUpdate);
+
+    if (hasBlendModeChanged)
+      setNeedsPaintPropertyUpdate();
   }
 
   LayoutObject::styleDidChange(diff, oldStyle);
diff --git a/third_party/WebKit/Source/core/paint/FindPropertiesNeedingUpdate.h b/third_party/WebKit/Source/core/paint/FindPropertiesNeedingUpdate.h
index a1578ca..0df5618 100644
--- a/third_party/WebKit/Source/core/paint/FindPropertiesNeedingUpdate.h
+++ b/third_party/WebKit/Source/core/paint/FindPropertiesNeedingUpdate.h
@@ -187,10 +187,12 @@
                                   originalBorderBox->propertyTreeState.scroll(),
                                   objectBorderBox->propertyTreeState.scroll());
       } else {
-        DCHECK_EQ(!!originalBorderBox, !!objectBorderBox);
+        DCHECK_EQ(!!originalBorderBox, !!objectBorderBox)
+            << " Object: " << m_object.debugName();
       }
     } else {
-      DCHECK_EQ(!!m_originalProperties, !!objectProperties);
+      DCHECK_EQ(!!m_originalProperties, !!objectProperties)
+          << " Object: " << m_object.debugName();
     }
     // Restore original clean bit.
     m_object.getMutableForPainting().clearNeedsPaintPropertyUpdateForTesting();
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index 970f656..a04ce99 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -138,7 +138,6 @@
 #if DCHECK_IS_ON()
       m_needsPositionUpdate(true),
 #endif
-      m_is3DTransformedDescendantDirty(true),
       m_has3DTransformedDescendant(false),
       m_containsDirtyOverlayScrollbars(false),
       m_needsAncestorDependentCompositingInputsUpdate(true),
@@ -415,7 +414,7 @@
   updateTransformationMatrix();
 
   if (had3DTransform != has3DTransform())
-    dirty3DTransformedDescendantStatus();
+    markAncestorChainForDescendantDependentFlagsUpdate();
 
   if (FrameView* frameView = layoutObject()->document().view())
     frameView->setNeedsUpdateWidgetGeometries();
@@ -743,51 +742,37 @@
     // this LayoutObject during the invalidateTreeIfNeeded walk.
     m_layoutObject->setMayNeedPaintInvalidation();
   }
+
+  update3DTransformedDescendantStatus();
 }
 
-void PaintLayer::dirty3DTransformedDescendantStatus() {
-  PaintLayerStackingNode* stackingNode =
-      m_stackingNode->ancestorStackingContextNode();
-  if (!stackingNode)
-    return;
+void PaintLayer::update3DTransformedDescendantStatus() {
+  m_has3DTransformedDescendant = false;
 
-  stackingNode->layer()->m_is3DTransformedDescendantDirty = true;
+  m_stackingNode->updateZOrderLists();
 
-  // This propagates up through preserve-3d hierarchies to the enclosing
-  // flattening layer.  Note that preserves3D() creates stacking context, so we
-  // can just run up the stacking containers.
-  while (stackingNode && stackingNode->layer()->preserves3D()) {
-    stackingNode->layer()->m_is3DTransformedDescendantDirty = true;
-    stackingNode = stackingNode->ancestorStackingContextNode();
+  // Transformed or preserve-3d descendants can only be in the z-order lists,
+  // not in the normal flow list, so we only need to check those.
+  PaintLayerStackingNodeIterator iterator(
+      *m_stackingNode.get(), PositiveZOrderChildren | NegativeZOrderChildren);
+  while (PaintLayerStackingNode* node = iterator.next()) {
+    const PaintLayer& childLayer = *node->layer();
+    bool childHas3D = false;
+    // If the child lives in a 3d hierarchy, then the layer at the root of
+    // that hierarchy needs the m_has3DTransformedDescendant set.
+    if (childLayer.preserves3D() && (childLayer.has3DTransform() ||
+                                     childLayer.has3DTransformedDescendant()))
+      childHas3D = true;
+    else if (childLayer.has3DTransform())
+      childHas3D = true;
+
+    if (childHas3D) {
+      m_has3DTransformedDescendant = true;
+      break;
+    }
   }
 }
 
-// Return true if this layer or any preserve-3d descendants have 3d.
-bool PaintLayer::update3DTransformedDescendantStatus() {
-  if (m_is3DTransformedDescendantDirty) {
-    m_has3DTransformedDescendant = false;
-
-    m_stackingNode->updateZOrderLists();
-
-    // Transformed or preserve-3d descendants can only be in the z-order lists,
-    // not in the normal flow list, so we only need to check those.
-    PaintLayerStackingNodeIterator iterator(
-        *m_stackingNode.get(), PositiveZOrderChildren | NegativeZOrderChildren);
-    while (PaintLayerStackingNode* node = iterator.next())
-      m_has3DTransformedDescendant |=
-          node->layer()->update3DTransformedDescendantStatus();
-
-    m_is3DTransformedDescendantDirty = false;
-  }
-
-  // If we live in a 3d hierarchy, then the layer at the root of that hierarchy
-  // needs the m_has3DTransformedDescendant set.
-  if (preserves3D())
-    return has3DTransform() || m_has3DTransformedDescendant;
-
-  return has3DTransform();
-}
-
 void PaintLayer::updateLayerPosition() {
   LayoutPoint localPoint;
 
@@ -2715,6 +2700,12 @@
 }
 
 bool PaintLayer::paintsWithTransform(GlobalPaintFlags globalPaintFlags) const {
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+    return transform() &&
+           ((globalPaintFlags & GlobalPaintFlattenCompositingLayers) ||
+            compositingState() != PaintsIntoOwnBacking);
+  }
+
   return (transform() ||
           layoutObject()->style()->position() == FixedPosition) &&
          ((globalPaintFlags & GlobalPaintFlattenCompositingLayers) ||
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index 5ec98e3..06de196 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -967,11 +967,8 @@
   }
   void clearClipRectsCache() const { m_clipRectsCache.reset(); }
 
-  void dirty3DTransformedDescendantStatus();
-  // Both updates the status, and returns true if descendants of this have 3d.
-  bool update3DTransformedDescendantStatus();
   bool has3DTransformedDescendant() const {
-    DCHECK(!m_is3DTransformedDescendantDirty);
+    DCHECK(!m_needsDescendantDependentFlagsUpdate);
     return m_has3DTransformedDescendant;
   }
 
@@ -982,6 +979,8 @@
  private:
   void setNeedsCompositingInputsUpdateInternal();
 
+  void update3DTransformedDescendantStatus();
+
   // Bounding box in the coordinates of this layer.
   LayoutRect logicalBoundingBox() const;
 
@@ -1139,7 +1138,6 @@
   unsigned m_needsPositionUpdate : 1;
 #endif
 
-  unsigned m_is3DTransformedDescendantDirty : 1;
   // Set on a stacking context layer that has 3D descendants anywhere
   // in a preserves3D hierarchy. Hint to do 3D-aware hit testing.
   unsigned m_has3DTransformedDescendant : 1;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
index 4c0351a..fe6bd4d 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -421,18 +421,20 @@
     // SPv2.  Related thread
     // https://groups.google.com/a/chromium.org/forum/#!topic/graphics-dev/81XuWFf-mxM
     if (fragmentPolicy == ForceSingleFragment ||
-        RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+        RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
       m_paintLayer.appendSingleFragmentIgnoringPagination(
           layerFragments, localPaintingInfo.rootLayer,
           localPaintingInfo.paintDirtyRect, cacheSlot,
           IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot,
           localPaintingInfo.subPixelAccumulation);
-    else
+    } else if (!collectPaintFragmentsForPaginatedFixedPosition(
+                   paintingInfo, layerFragments)) {
       m_paintLayer.collectFragments(layerFragments, localPaintingInfo.rootLayer,
                                     localPaintingInfo.paintDirtyRect, cacheSlot,
                                     IgnoreOverlayScrollbarSize,
                                     respectOverflowClip, &offsetFromRoot,
                                     localPaintingInfo.subPixelAccumulation);
+    }
 
     if (shouldPaintContent) {
       // TODO(wangxianzhu): This is for old slow scrolling. Implement similar
@@ -589,6 +591,42 @@
   return false;
 }
 
+bool PaintLayerPainter::collectPaintFragmentsForPaginatedFixedPosition(
+    const PaintLayerPaintingInfo& paintingInfo,
+    PaintLayerFragments& layerFragments) {
+  LayoutObject* object = m_paintLayer.layoutObject();
+  LayoutView* view = object->view();
+  bool isFixedPosObjectInPagedMedia =
+      object->style()->position() == FixedPosition &&
+      object->container() == view && view->pageLogicalHeight();
+
+  // TODO(crbug.com/619094): Figure out the correct behaviour for fixed position
+  // objects in paged media with vertical writing modes.
+  if (!isFixedPosObjectInPagedMedia || !view->isHorizontalWritingMode())
+    return false;
+
+  // "For paged media, boxes with fixed positions are repeated on every page."
+  // https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html#fixed-positioning
+  unsigned pages =
+      ceilf(view->documentRect().height() / view->pageLogicalHeight());
+  LayoutPoint paginationOffset;
+
+  // The fixed position object is offset from the top of the page, so remove
+  // any scroll offset.
+  LayoutPoint offsetFromRoot;
+  m_paintLayer.convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot);
+  paginationOffset -= offsetFromRoot - m_paintLayer.location();
+
+  for (unsigned i = 0; i < pages; i++) {
+    PaintLayerFragment fragment;
+    fragment.backgroundRect = paintingInfo.paintDirtyRect;
+    fragment.paginationOffset = paginationOffset;
+    layerFragments.append(fragment);
+    paginationOffset += LayoutPoint(LayoutUnit(), view->pageLogicalHeight());
+  }
+  return true;
+}
+
 PaintResult PaintLayerPainter::paintLayerWithTransform(
     GraphicsContext& context,
     const PaintLayerPaintingInfo& paintingInfo,
@@ -610,62 +648,46 @@
       object->style()->position() == FixedPosition &&
       object->container() == view && view->pageLogicalHeight();
   PaintLayer* paginationLayer = m_paintLayer.enclosingPaginationLayer();
-  PaintLayerFragments fragments;
-  // TODO(crbug.com/619094): Figure out the correct behaviour for fixed position
-  // objects in paged media with vertical writing modes.
-  if (isFixedPosObjectInPagedMedia && view->isHorizontalWritingMode()) {
-    // "For paged media, boxes with fixed positions are repeated on every page."
-    // https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html#fixed-positioning
-    unsigned pages =
-        ceilf(view->documentRect().height() / view->pageLogicalHeight());
-    LayoutPoint paginationOffset;
-
-    // The fixed position object is offset from the top of the page, so remove
-    // any scroll offset.
-    LayoutPoint offsetFromRoot;
-    m_paintLayer.convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot);
-    paginationOffset -= offsetFromRoot - m_paintLayer.location();
-
-    for (unsigned i = 0; i < pages; i++) {
+  PaintLayerFragments layerFragments;
+  if (!collectPaintFragmentsForPaginatedFixedPosition(paintingInfo,
+                                                      layerFragments)) {
+    if (paginationLayer) {
+      // FIXME: This is a mess. Look closely at this code and the code in Layer
+      // and fix any issues in it & refactor to make it obvious from code
+      // structure what it does and that it's correct.
+      ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects)
+                                         ? UncachedClipRects
+                                         : PaintingClipRects;
+      ShouldRespectOverflowClipType respectOverflowClip =
+          shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject());
+      // Calculate the transformed bounding box in the current coordinate space,
+      // to figure out which fragmentainers (e.g. columns) we need to visit.
+      LayoutRect transformedExtent = PaintLayer::transparencyClipBox(
+          &m_paintLayer, paginationLayer,
+          PaintLayer::PaintingTransparencyClipBox,
+          PaintLayer::RootOfTransparencyClipBox,
+          paintingInfo.subPixelAccumulation,
+          paintingInfo.getGlobalPaintFlags());
+      // FIXME: we don't check if paginationLayer is within
+      // paintingInfo.rootLayer
+      // here.
+      paginationLayer->collectFragments(
+          layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect,
+          cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, 0,
+          paintingInfo.subPixelAccumulation, &transformedExtent);
+    } else {
+      // We don't need to collect any fragments in the regular way here. We have
+      // already calculated a clip rectangle for the ancestry if it was needed,
+      // and clipping this layer is something that can be done further down the
+      // path, when the transform has been applied.
       PaintLayerFragment fragment;
       fragment.backgroundRect = paintingInfo.paintDirtyRect;
-      fragment.paginationOffset = paginationOffset;
-      fragments.append(fragment);
-      paginationOffset += LayoutPoint(LayoutUnit(), view->pageLogicalHeight());
+      layerFragments.append(fragment);
     }
-  } else if (paginationLayer) {
-    // FIXME: This is a mess. Look closely at this code and the code in Layer
-    // and fix any issues in it & refactor to make it obvious from code
-    // structure what it does and that it's correct.
-    ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects)
-                                       ? UncachedClipRects
-                                       : PaintingClipRects;
-    ShouldRespectOverflowClipType respectOverflowClip =
-        shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject());
-    // Calculate the transformed bounding box in the current coordinate space,
-    // to figure out which fragmentainers (e.g. columns) we need to visit.
-    LayoutRect transformedExtent = PaintLayer::transparencyClipBox(
-        &m_paintLayer, paginationLayer, PaintLayer::PaintingTransparencyClipBox,
-        PaintLayer::RootOfTransparencyClipBox,
-        paintingInfo.subPixelAccumulation, paintingInfo.getGlobalPaintFlags());
-    // FIXME: we don't check if paginationLayer is within paintingInfo.rootLayer
-    // here.
-    paginationLayer->collectFragments(
-        fragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect,
-        cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, 0,
-        paintingInfo.subPixelAccumulation, &transformedExtent);
-  } else {
-    // We don't need to collect any fragments in the regular way here. We have
-    // already calculated a clip rectangle for the ancestry if it was needed,
-    // and clipping this layer is something that can be done further down the
-    // path, when the transform has been applied.
-    PaintLayerFragment fragment;
-    fragment.backgroundRect = paintingInfo.paintDirtyRect;
-    fragments.append(fragment);
   }
 
   Optional<DisplayItemCacheSkipper> cacheSkipper;
-  if (fragments.size() > 1)
+  if (layerFragments.size() > 1)
     cacheSkipper.emplace(context);
 
   ClipRect ancestorBackgroundClipRect;
@@ -686,7 +708,7 @@
   }
 
   PaintResult result = FullyPainted;
-  for (const auto& fragment : fragments) {
+  for (const auto& fragment : layerFragments) {
     Optional<LayerClipRecorder> clipRecorder;
     if (parentLayer && !RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
       ClipRect clipRectForFragment(ancestorBackgroundClipRect);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.h b/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
index de25ce2..3e9f3494a 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.h
@@ -56,6 +56,10 @@
  private:
   enum ClipState { HasNotClipped, HasClipped };
 
+  bool collectPaintFragmentsForPaginatedFixedPosition(
+      const PaintLayerPaintingInfo&,
+      PaintLayerFragments&);
+
   PaintResult paintLayerContentsCompositingAllPhases(
       GraphicsContext&,
       const PaintLayerPaintingInfo&,
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
index aa3d293a..0c5802f 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
@@ -290,6 +290,84 @@
   EXPECT_FALSE(invisible->hasDescendantWithClipPath());
 }
 
+TEST_P(PaintLayerTest, Has3DTransformedDescendant) {
+  enableCompositing();
+  setBodyInnerHTML(
+      "<div id='parent' style='position:relative; z-index: 0'>"
+      "  <div id='child' style='transform: translateZ(1px)'>"
+      "  </div>"
+      "</div>");
+  PaintLayer* parent =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("parent"))->layer();
+  PaintLayer* child =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("child"))->layer();
+
+  EXPECT_TRUE(parent->has3DTransformedDescendant());
+  EXPECT_FALSE(child->has3DTransformedDescendant());
+}
+
+TEST_P(PaintLayerTest, Has3DTransformedDescendantChangeStyle) {
+  enableCompositing();
+  setBodyInnerHTML(
+      "<div id='parent' style='position:relative; z-index: 0'>"
+      "  <div id='child' style='position:relative '>"
+      "  </div>"
+      "</div>");
+  PaintLayer* parent =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("parent"))->layer();
+  PaintLayer* child =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("child"))->layer();
+
+  EXPECT_FALSE(parent->has3DTransformedDescendant());
+  EXPECT_FALSE(child->has3DTransformedDescendant());
+
+  document().getElementById("child")->setAttribute(
+      HTMLNames::styleAttr, "transform: translateZ(1px)");
+  document().view()->updateAllLifecyclePhases();
+
+  EXPECT_TRUE(parent->has3DTransformedDescendant());
+  EXPECT_FALSE(child->has3DTransformedDescendant());
+}
+
+TEST_P(PaintLayerTest, Has3DTransformedDescendantNotStacking) {
+  enableCompositing();
+  setBodyInnerHTML(
+      "<div id='parent' style='position:relative;'>"
+      "  <div id='child' style='transform: translateZ(1px)'>"
+      "  </div>"
+      "</div>");
+  PaintLayer* parent =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("parent"))->layer();
+  PaintLayer* child =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("child"))->layer();
+
+  // |child| is not a stacking child of |parent|, so it has no 3D transformed
+  // descendant.
+  EXPECT_FALSE(parent->has3DTransformedDescendant());
+  EXPECT_FALSE(child->has3DTransformedDescendant());
+}
+
+TEST_P(PaintLayerTest, Has3DTransformedGrandchildWithPreserve3d) {
+  enableCompositing();
+  setBodyInnerHTML(
+      "<div id='parent' style='position:relative; z-index: 0'>"
+      "  <div id='child' style='transform-style: preserve-3d'>"
+      "    <div id='grandchild' style='transform: translateZ(1px)'>"
+      "    </div>"
+      "  </div>"
+      "</div>");
+  PaintLayer* parent =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("parent"))->layer();
+  PaintLayer* child =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("child"))->layer();
+  PaintLayer* grandchild =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("grandchild"))->layer();
+
+  EXPECT_TRUE(parent->has3DTransformedDescendant());
+  EXPECT_TRUE(child->has3DTransformedDescendant());
+  EXPECT_FALSE(grandchild->has3DTransformedDescendant());
+}
+
 TEST_P(PaintLayerTest, DescendantDependentFlagsStopsAtThrottledFrames) {
   enableCompositing();
   setBodyInnerHTML(
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index 6baebf0..73f96a4 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -219,10 +219,6 @@
     usesPaintOffsetTranslation = true;
   } else if (object.isBoxModelObject() &&
              context.current.paintOffset != LayoutPoint()) {
-    // TODO(trchen): Eliminate PaintLayer dependency.
-    // TODO(chrishtr): When changing the condition here, make sure to update
-    // the condition in LayoutBoxModelObject::styleDidChange() above
-    // setNeedsPaintPropertyUpdate().
     PaintLayer* layer = toLayoutBoxModelObject(object).layer();
     if (layer &&
         layer->paintsWithTransform(GlobalPaintFlattenCompositingLayers))
@@ -305,30 +301,24 @@
   }
 }
 
-static CompositingReasons compositingReasonsForTransform(
-    const LayoutObject& object) {
+static CompositingReasons compositingReasonsForTransform(const LayoutBox& box) {
+  const ComputedStyle& style = box.styleRef();
   CompositingReasons compositingReasons = CompositingReasonNone;
-  if (CompositingReasonFinder::requiresCompositingForTransform(object))
+  if (CompositingReasonFinder::requiresCompositingForTransform(box))
     compositingReasons |= CompositingReason3DTransform;
 
-  if (CompositingReasonFinder::requiresCompositingForTransformAnimation(
-          object.styleRef()))
+  if (CompositingReasonFinder::requiresCompositingForTransformAnimation(style))
     compositingReasons |= CompositingReasonActiveAnimation;
 
-  if (object.styleRef().hasWillChangeCompositingHint() &&
-      !object.styleRef().subtreeWillChangeContents())
+  if (style.hasWillChangeCompositingHint() &&
+      !style.subtreeWillChangeContents())
     compositingReasons |= CompositingReasonWillChangeCompositingHint;
 
-  if (object.isBoxModelObject()) {
-    const LayoutBoxModelObject* box = toLayoutBoxModelObject(&object);
-    if (box->hasLayer()) {
-      // TODO(chrishtr): move this to the descendant-dependent flags recursion
-      // PaintLayer::updateDescendantDependentFlags.
-      box->layer()->update3DTransformedDescendantStatus();
-
-      if (box->layer()->has3DTransformedDescendant())
-        compositingReasons |= CompositingReason3DTransform;
-    }
+  if (box.hasLayer() && box.layer()->has3DTransformedDescendant()) {
+    if (style.hasPerspective())
+      compositingReasons |= CompositingReasonPerspectiveWith3DDescendants;
+    if (style.usedTransformStyle3D() == TransformStyle3DPreserve3D)
+      compositingReasons |= CompositingReasonPreserve3DWith3DDescendants;
   }
 
   return compositingReasons;
@@ -357,36 +347,44 @@
   if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
     const ComputedStyle& style = object.styleRef();
 
-    CompositingReasons compositingReasons =
-        compositingReasonsForTransform(object);
-
     // A transform node is allocated for transforms, preserves-3d and any
     // direct compositing reason. The latter is required because this is the
     // only way to represent compositing both an element and its stacking
     // descendants.
-    if (object.isBox() && (style.hasTransform() || style.preserves3D() ||
-                           compositingReasons != CompositingReasonNone)) {
+    bool hasTransform = false;
+    if (object.isBox()) {
       auto& box = toLayoutBox(object);
-      TransformationMatrix matrix;
-      style.applyTransform(
-          matrix, box.size(), ComputedStyle::ExcludeTransformOrigin,
-          ComputedStyle::IncludeMotionPath,
-          ComputedStyle::IncludeIndependentTransformProperties);
 
-      // TODO(trchen): transform-style should only be respected if a PaintLayer
-      // is created.
-      // If a node with transform-style: preserve-3d does not exist in an
-      // existing rendering context, it establishes a new one.
-      unsigned renderingContextID = context.current.renderingContextID;
-      if (style.preserves3D() && !renderingContextID)
-        renderingContextID = PtrHash<const LayoutObject>::hash(&object);
+      CompositingReasons compositingReasons =
+          compositingReasonsForTransform(box);
 
-      auto& properties = object.getMutableForPainting().ensurePaintProperties();
-      context.forceSubtreeUpdate |= properties.updateTransform(
-          context.current.transform, matrix, transformOrigin(box),
-          context.current.shouldFlattenInheritedTransform, renderingContextID,
-          compositingReasons);
-    } else {
+      if (style.hasTransform() || style.preserves3D() ||
+          compositingReasons != CompositingReasonNone) {
+        TransformationMatrix matrix;
+        style.applyTransform(
+            matrix, box.size(), ComputedStyle::ExcludeTransformOrigin,
+            ComputedStyle::IncludeMotionPath,
+            ComputedStyle::IncludeIndependentTransformProperties);
+
+        // TODO(trchen): transform-style should only be respected if a
+        // PaintLayer
+        // is created.
+        // If a node with transform-style: preserve-3d does not exist in an
+        // existing rendering context, it establishes a new one.
+        unsigned renderingContextID = context.current.renderingContextID;
+        if (style.preserves3D() && !renderingContextID)
+          renderingContextID = PtrHash<const LayoutObject>::hash(&object);
+
+        auto& properties =
+            object.getMutableForPainting().ensurePaintProperties();
+        context.forceSubtreeUpdate |= properties.updateTransform(
+            context.current.transform, matrix, transformOrigin(box),
+            context.current.shouldFlattenInheritedTransform, renderingContextID,
+            compositingReasons);
+        hasTransform = true;
+      }
+    }
+    if (!hasTransform) {
       if (auto* properties = object.getMutableForPainting().paintProperties())
         context.forceSubtreeUpdate |= properties->clearTransform();
     }
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index 10952ba..c074b664 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -140,18 +140,12 @@
   Element* target1 = document().getElementById("target1");
   const ObjectPaintProperties* target1Properties =
       target1->layoutObject()->paintProperties();
-  EXPECT_EQ(TransformationMatrix().translate(200, 150),
-            target1Properties->paintOffsetTranslation()->matrix());
-  EXPECT_EQ(framePreTranslation(),
-            target1Properties->paintOffsetTranslation()->parent());
-  EXPECT_EQ(target1Properties->paintOffsetTranslation(),
-            target1Properties->overflowClip()->localTransformSpace());
-  EXPECT_EQ(FloatRoundedRect(0, 0, 100, 100),
+  EXPECT_EQ(FloatRoundedRect(200, 150, 100, 100),
             target1Properties->overflowClip()->clipRect());
   // Likewise, it inherits clip from the viewport, skipping overflow clip of the
   // scroller.
   EXPECT_EQ(frameContentClip(), target1Properties->overflowClip()->parent());
-  // target1 should not have it's own scroll node and instead should inherit
+  // target1 should not have its own scroll node and instead should inherit
   // positionedScroll's.
   const ObjectPaintProperties* positionedScrollProperties =
       positionedScroll->layoutObject()->paintProperties();
@@ -161,7 +155,6 @@
                 ->scrollOffsetTranslation()
                 ->matrix());
   EXPECT_EQ(nullptr, target1Properties->scroll());
-
   CHECK_EXACT_VISUAL_RECT(LayoutRect(200, 150, 100, 100),
                           target1->layoutObject(), frameView->layoutView());
 
@@ -173,13 +166,7 @@
   Element* scroller = document().getElementById("transformedScroll");
   const ObjectPaintProperties* scrollerProperties =
       scroller->layoutObject()->paintProperties();
-  EXPECT_EQ(TransformationMatrix().translate(200, 150),
-            target2Properties->paintOffsetTranslation()->matrix());
-  EXPECT_EQ(scrollerProperties->scrollTranslation(),
-            target2Properties->paintOffsetTranslation()->parent());
-  EXPECT_EQ(target2Properties->paintOffsetTranslation(),
-            target2Properties->overflowClip()->localTransformSpace());
-  EXPECT_EQ(FloatRoundedRect(0, 0, 100, 100),
+  EXPECT_EQ(FloatRoundedRect(200, 150, 100, 100),
             target2Properties->overflowClip()->clipRect());
   EXPECT_EQ(scrollerProperties->overflowClip(),
             target2Properties->overflowClip()->parent());
@@ -360,6 +347,7 @@
   Element* transform = document().getElementById("transform");
   const ObjectPaintProperties* transformProperties =
       transform->layoutObject()->paintProperties();
+
   EXPECT_EQ(TransformationMatrix().translate3d(123, 456, 789),
             transformProperties->transform()->matrix());
   EXPECT_EQ(FloatPoint3D(200, 150, 0),
@@ -394,6 +382,43 @@
       transform->layoutObject()->paintProperties()->transform()->matrix());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, Preserve3D3DTransformedDescendant) {
+  setBodyInnerHTML(
+      "<style> body { margin: 0 } </style>"
+      "<div id='preserve' style='transform-style: preserve-3d'>"
+      "<div id='transform' style='margin-left: 50px; margin-top: 100px;"
+      "    width: 400px; height: 300px;"
+      "    transform: translate3d(123px, 456px, 789px)'>"
+      "</div>"
+      "</div>");
+
+  Element* preserve = document().getElementById("preserve");
+  const ObjectPaintProperties* preserveProperties =
+      preserve->layoutObject()->paintProperties();
+
+  EXPECT_TRUE(preserveProperties->transform());
+  EXPECT_TRUE(preserveProperties->transform()->hasDirectCompositingReasons());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, Perspective3DTransformedDescendant) {
+  setBodyInnerHTML(
+      "<style> body { margin: 0 } </style>"
+      "<div id='perspective' style='perspective: 800px;'>"
+      "<div id='transform' style='margin-left: 50px; margin-top: 100px;"
+      "    width: 400px; height: 300px;"
+      "    transform: translate3d(123px, 456px, 789px)'>"
+      "</div>"
+      "</div>");
+
+  Element* perspective = document().getElementById("perspective");
+  const ObjectPaintProperties* perspectiveProperties =
+      perspective->layoutObject()->paintProperties();
+
+  EXPECT_TRUE(perspectiveProperties->transform());
+  EXPECT_TRUE(
+      perspectiveProperties->transform()->hasDirectCompositingReasons());
+}
+
 TEST_P(PaintPropertyTreeBuilderTest,
        TransformNodeWithActiveAnimationHasDirectCompositingReason) {
   setBodyInnerHTML(
@@ -1049,12 +1074,11 @@
   Element* fixed = document().getElementById("fixed");
   const ObjectPaintProperties* fixedProperties =
       fixed->layoutObject()->paintProperties();
-  EXPECT_EQ(TransformationMatrix().translate(200, 150),
-            fixedProperties->paintOffsetTranslation()->matrix());
   // Ensure the fixed position element is rooted at the nearest transform
   // container.
   EXPECT_EQ(containerProperties->transform(),
-            fixedProperties->paintOffsetTranslation()->parent());
+            fixedProperties->localBorderBoxProperties()
+                ->propertyTreeState.transform());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, ControlClip) {
@@ -1447,13 +1471,8 @@
       clipProperties->cssClip(),
       fixedProperties->localBorderBoxProperties()->propertyTreeState.clip());
   EXPECT_EQ(framePreTranslation(), fixedProperties->localBorderBoxProperties()
-                                       ->propertyTreeState.transform()
-                                       ->parent());
-  EXPECT_EQ(TransformationMatrix().translate(654, 321),
-            fixedProperties->localBorderBoxProperties()
-                ->propertyTreeState.transform()
-                ->matrix());
-  EXPECT_EQ(LayoutPoint(),
+                                       ->propertyTreeState.transform());
+  EXPECT_EQ(LayoutPoint(654, 321),
             fixedProperties->localBorderBoxProperties()->paintOffset);
   CHECK_VISUAL_RECT(LayoutRect(), fixed, document().view()->layoutView(),
                     // TODO(crbug.com/599939): CSS clip of fixed-position
@@ -1589,13 +1608,8 @@
       clipProperties->cssClipFixedPosition(),
       fixedProperties->localBorderBoxProperties()->propertyTreeState.clip());
   EXPECT_EQ(framePreTranslation(), fixedProperties->localBorderBoxProperties()
-                                       ->propertyTreeState.transform()
-                                       ->parent());
-  EXPECT_EQ(TransformationMatrix().translate(654, 321),
-            fixedProperties->localBorderBoxProperties()
-                ->propertyTreeState.transform()
-                ->matrix());
-  EXPECT_EQ(LayoutPoint(),
+                                       ->propertyTreeState.transform());
+  EXPECT_EQ(LayoutPoint(654, 321),
             fixedProperties->localBorderBoxProperties()->paintOffset);
   CHECK_VISUAL_RECT(LayoutRect(), fixed, document().view()->layoutView(),
                     // TODO(crbug.com/599939): CSS clip of fixed-position
@@ -3078,10 +3092,7 @@
       getLayoutObjectByElementId("child")->paintProperties();
   const PropertyTreeState& childPaintState =
       childProperties->localBorderBoxProperties()->propertyTreeState;
-  EXPECT_EQ(framePreTranslation(),
-            childProperties->paintOffsetTranslation()->parent());
-  EXPECT_EQ(childProperties->paintOffsetTranslation(),
-            childPaintState.transform());
+
   // This will change once we added clip expansion node.
   EXPECT_EQ(filterProperties->effect()->outputClip(), childPaintState.clip());
   EXPECT_EQ(filterProperties->effect(), childPaintState.effect());
diff --git a/third_party/WebKit/Source/platform/graphics/CompositingReasons.cpp b/third_party/WebKit/Source/platform/graphics/CompositingReasons.cpp
index a50fe42..9f883da 100644
--- a/third_party/WebKit/Source/platform/graphics/CompositingReasons.cpp
+++ b/third_party/WebKit/Source/platform/graphics/CompositingReasons.cpp
@@ -5,6 +5,7 @@
 #include "platform/graphics/CompositingReasons.h"
 
 #include "wtf/StdLibExtras.h"
+#include "wtf/text/StringBuilder.h"
 
 namespace blink {
 
@@ -141,4 +142,19 @@
 const size_t kNumberOfCompositingReasons =
     WTF_ARRAY_LENGTH(kCompositingReasonStringMap);
 
+String compositingReasonsAsString(CompositingReasons reasons) {
+  if (!reasons)
+    return "none";
+
+  StringBuilder builder;
+  for (size_t i = 0; i < kNumberOfCompositingReasons; ++i) {
+    if (reasons & kCompositingReasonStringMap[i].reason) {
+      if (builder.length())
+        builder.append(',');
+      builder.append(kCompositingReasonStringMap[i].shortName);
+    }
+  }
+  return builder.toString();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/CompositingReasons.h b/third_party/WebKit/Source/platform/graphics/CompositingReasons.h
index e0d8667..b322682 100644
--- a/third_party/WebKit/Source/platform/graphics/CompositingReasons.h
+++ b/third_party/WebKit/Source/platform/graphics/CompositingReasons.h
@@ -7,6 +7,7 @@
 
 #include "platform/PlatformExport.h"
 #include "wtf/Allocator.h"
+#include "wtf/text/WTFString.h"
 #include <stdint.h>
 
 namespace blink {
@@ -181,6 +182,7 @@
 PLATFORM_EXPORT extern const CompositingReasonStringMap
     kCompositingReasonStringMap[];
 PLATFORM_EXPORT extern const size_t kNumberOfCompositingReasons;
+PLATFORM_EXPORT String compositingReasonsAsString(CompositingReasons);
 
 }  // namespace blink
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.cpp b/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.cpp
index 85f8fe4..ced22943 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.cpp
@@ -17,9 +17,11 @@
 }
 
 String ClipPaintPropertyNode::toString() const {
-  return String::format("localTransformSpace=%p rect=%s",
-                        m_localTransformSpace.get(),
-                        m_clipRect.toString().ascii().data());
+  return String::format(
+      "parent=%p localTransformSpace=%p rect=%s directCompositingReasons=%s",
+      m_parent.get(), m_localTransformSpace.get(),
+      m_clipRect.toString().ascii().data(),
+      compositingReasonsAsString(m_directCompositingReasons).ascii().data());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.h
index 80ec78512..8e0667b3 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPaintPropertyNode.h
@@ -71,7 +71,8 @@
   bool operator==(const ClipPaintPropertyNode& o) const {
     return m_parent == o.m_parent &&
            m_localTransformSpace == o.m_localTransformSpace &&
-           m_clipRect == o.m_clipRect;
+           m_clipRect == o.m_clipRect &&
+           m_directCompositingReasons == o.m_directCompositingReasons;
   }
 #endif
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.cpp b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.cpp
index 4c5c8c8..85a7293 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.cpp
@@ -25,9 +25,12 @@
 
 String EffectPaintPropertyNode::toString() const {
   return String::format(
-      "localTransformSpace=%p outputClip=%p opacity=%f filter=%s blendMode=%s",
-      m_localTransformSpace.get(), m_outputClip.get(), m_opacity,
-      m_filter.toString().ascii().data(), SkBlendMode_Name(m_blendMode));
+      "parent=%p localTransformSpace=%p outputClip=%p opacity=%f filter=%s "
+      "blendMode=%s directCompositingReasons=%s",
+      m_parent.get(), m_localTransformSpace.get(), m_outputClip.get(),
+      m_opacity, m_filter.toString().ascii().data(),
+      SkBlendMode_Name(m_blendMode),
+      compositingReasonsAsString(m_directCompositingReasons).ascii().data());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
index cebd0ea..ec1a562 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/EffectPaintPropertyNode.h
@@ -92,9 +92,10 @@
   bool operator==(const EffectPaintPropertyNode& o) const {
     return m_parent == o.m_parent &&
            m_localTransformSpace == o.m_localTransformSpace &&
-           m_outputClip == o.m_outputClip && m_opacity == o.m_opacity &&
-           m_blendMode == o.m_blendMode &&
-           m_filter.equalsIgnoringReferenceFilters(o.m_filter);
+           m_outputClip == o.m_outputClip &&
+           m_filter.equalsIgnoringReferenceFilters(o.m_filter) &&
+           m_opacity == o.m_opacity && m_blendMode == o.m_blendMode &&
+           m_directCompositingReasons == o.m_directCompositingReasons;
   }
 #endif
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.cpp b/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.cpp
index accb40e3..f9b87e54f 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.cpp
@@ -21,10 +21,11 @@
       MainThreadScrollingReason::mainThreadScrollingReasonsAsText(
           m_mainThreadScrollingReasons);
   return String::format(
-      "scrollOffsetTranslation=%s clip=%s bounds=%s userScrollableHorizontal=%s"
+      "parent=%p scrollOffsetTranslation=%s clip=%s bounds=%s "
+      "userScrollableHorizontal=%s"
       " userScrollableVertical=%s mainThreadScrollingReasons=%s",
-      scrollOffset.toString().ascii().data(), m_clip.toString().ascii().data(),
-      m_bounds.toString().ascii().data(),
+      m_parent.get(), scrollOffset.toString().ascii().data(),
+      m_clip.toString().ascii().data(), m_bounds.toString().ascii().data(),
       m_userScrollableHorizontal ? "yes" : "no",
       m_userScrollableVertical ? "yes" : "no",
       mainThreadScrollingReasonsAsText.c_str());
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.cpp b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.cpp
index 5ddf718..fe547f1 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.cpp
@@ -14,7 +14,13 @@
 }
 
 String TransformPaintPropertyNode::toString() const {
-  return "transform=" + m_matrix.toString() + " origin=" + m_origin.toString();
+  return String::format(
+      "parent=%p transform=%s origin=%s flattensInheritedTransform=%s "
+      "renderContextID=%x directCompositingReasons=%s",
+      m_parent.get(), m_matrix.toString().ascii().data(),
+      m_origin.toString().ascii().data(),
+      m_flattensInheritedTransform ? "yes" : "no", m_renderingContextID,
+      compositingReasonsAsString(m_directCompositingReasons).ascii().data());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
index 0d2fb45f..1bde92d3 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
@@ -36,7 +36,7 @@
       unsigned renderingContextID = 0,
       CompositingReasons directCompositingReasons = CompositingReasonNone) {
     return adoptRef(new TransformPaintPropertyNode(
-        matrix, origin, std::move(parent), flattensInheritedTransform,
+        std::move(parent), matrix, origin, flattensInheritedTransform,
         renderingContextID, directCompositingReasons));
   }
 
@@ -86,17 +86,18 @@
   // a transform node before it has been updated, to later detect changes.
   PassRefPtr<TransformPaintPropertyNode> clone() const {
     return adoptRef(new TransformPaintPropertyNode(
-        m_matrix, m_origin, m_parent, m_flattensInheritedTransform,
+        m_parent, m_matrix, m_origin, m_flattensInheritedTransform,
         m_renderingContextID, m_directCompositingReasons));
   }
 
   // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
   // if a transform node has changed.
   bool operator==(const TransformPaintPropertyNode& o) const {
-    return m_matrix == o.m_matrix && m_origin == o.m_origin &&
-           m_parent == o.m_parent &&
+    return m_parent == o.m_parent && m_matrix == o.m_matrix &&
+           m_origin == o.m_origin &&
            m_flattensInheritedTransform == o.m_flattensInheritedTransform &&
-           m_renderingContextID == o.m_renderingContextID;
+           m_renderingContextID == o.m_renderingContextID &&
+           m_directCompositingReasons == o.m_directCompositingReasons;
   }
 #endif
 
@@ -104,22 +105,22 @@
 
  private:
   TransformPaintPropertyNode(
+      PassRefPtr<const TransformPaintPropertyNode> parent,
       const TransformationMatrix& matrix,
       const FloatPoint3D& origin,
-      PassRefPtr<const TransformPaintPropertyNode> parent,
       bool flattensInheritedTransform,
       unsigned renderingContextID,
       CompositingReasons directCompositingReasons)
-      : m_matrix(matrix),
+      : m_parent(parent),
+        m_matrix(matrix),
         m_origin(origin),
-        m_parent(parent),
         m_flattensInheritedTransform(flattensInheritedTransform),
         m_renderingContextID(renderingContextID),
         m_directCompositingReasons(directCompositingReasons) {}
 
+  RefPtr<const TransformPaintPropertyNode> m_parent;
   TransformationMatrix m_matrix;
   FloatPoint3D m_origin;
-  RefPtr<const TransformPaintPropertyNode> m_parent;
   bool m_flattensInheritedTransform;
   unsigned m_renderingContextID;
   CompositingReasons m_directCompositingReasons;
diff --git a/third_party/WebKit/public/web/WebPrintParams.h b/third_party/WebKit/public/web/WebPrintParams.h
index a28f836..800f2f8 100644
--- a/third_party/WebKit/public/web/WebPrintParams.h
+++ b/third_party/WebKit/public/web/WebPrintParams.h
@@ -51,6 +51,9 @@
   // Specifies user selected DPI for printing.
   int printerDPI;
 
+  // Specifies whether to print PDFs as image.
+  bool rasterizePDF = false;
+
   // Specifies whether to reduce/enlarge/retain the print contents to fit the
   // printable area. (This is used only by plugin printing).
   WebPrintScalingOption printScalingOption;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 984189d..0feacbcd 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -93987,6 +93987,7 @@
   <int value="147373243" label="enable-deferred-image-decoding"/>
   <int value="157217034" label="enable-tab-for-desktop-share"/>
   <int value="157318016" label="AutomaticTabDiscarding:enabled"/>
+  <int value="173288154" label="PrintPdfAsImage:enabled"/>
   <int value="178337215" label="enable-md-history"/>
   <int value="180074362" label="memory-pressure-thresholds"/>
   <int value="189728101" label="FasterLocationReload:disabled"/>
@@ -94078,9 +94079,11 @@
   <int value="606512202" label="AutofillCreditCardPopupLayout:enabled"/>
   <int value="609112512" label="touch-selection-strategy"/>
   <int value="610545308" label="enable-potentially-annoying-security-features"/>
+  <int value="624317932" label="print-pdf-as-image"/>
   <int value="625273056" label="disable-boot-animation"/>
   <int value="628302973" label="NTPSnippets:enabled"/>
   <int value="630947363" label="touch-events"/>
+  <int value="635971109" label="PrintPdfAsImage:disabled"/>
   <int value="636425179" label="mhtml-generator-option"/>
   <int value="637396292" label="AllBookmarks:enabled"/>
   <int value="643725031" label="disable-touch-feedback"/>
@@ -100023,6 +100026,7 @@
   <int value="16" label="NON_DEFAULT_MARGINS"/>
   <int value="17" label="DISTILL_PAGE_UNUSED"/>
   <int value="18" label="SCALING"/>
+  <int value="19" label="PRINT_AS_IMAGE"/>
 </enum>
 
 <enum name="PrivetNotificationsEvent" type="int">