diff --git a/BUILD.gn b/BUILD.gn
index 8992e33..53e2372 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -287,6 +287,7 @@
       "//chrome/test:telemetry_perf_unittests",
       "//chrome/test:unit_tests",
       "//components:components_browsertests",
+      "//components/policy:policy_templates",
       "//content/shell:content_shell",
       "//content/test:content_browsertests",
       "//content/test:content_perftests",
@@ -384,10 +385,6 @@
     deps += [ "//tools/xdisplaycheck" ]
   }
 
-  if (enable_configuration_policy) {
-    deps += [ "//components/policy:policy_templates" ]
-  }
-
   if (v8_use_external_startup_data) {
     deps += [ "//gin:gin_v8_snapshot_fingerprint" ]
   }
diff --git a/DEPS b/DEPS
index 86ef17b..6c45d03 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '1951f3ddbddcf5e7cdfe151981d13a1bdb6a3baa',
+  'skia_revision': '2790c52e36ddd8c46d8238f3c92f47779f79fb69',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '707aaac64b3c0a86d253e1ab502e73996d63927e',
+  'catapult_revision': '19565fdb148afc5fc752516f395f715f5d27c1f1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 6b17fd1..89c5b88 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -488,6 +488,8 @@
     "//components/metrics:profiler",
     "//components/metrics:ui",
     "//components/navigation_interception",
+    "//components/policy:generated",
+    "//components/policy/core/browser",
     "//components/prefs",
     "//components/printing/browser",
     "//components/printing/common",
@@ -526,13 +528,6 @@
     "//v8",
   ]
 
-  if (enable_configuration_policy) {
-    deps += [
-      "//components/policy:generated",
-      "//components/policy/core/browser",
-    ]
-  }
-
   if (enable_spellcheck) {
     deps += [
       "//components/spellcheck/browser",
@@ -617,6 +612,7 @@
     "//base:base_java",
     "//components/autofill/android:autofill_java",
     "//components/navigation_interception/android:navigation_interception_java",
+    "//components/policy/android:policy_java",
     "//components/web_contents_delegate_android:web_contents_delegate_android_java",
     "//components/web_restrictions:web_restrictions_java",
     "//content/public/android:content_java",
@@ -628,10 +624,6 @@
     google_play_services_resources,
   ]
 
-  if (enable_configuration_policy) {
-    deps += [ "//components/policy/android:policy_java" ]
-  }
-
   if (enable_spellcheck) {
     deps += [ "//components/spellcheck/browser/android:java" ]
   }
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index e28ad84..5764c4a 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -160,10 +160,6 @@
 void AshTestHelper::TearDown() {
   // Tear down the shell.
   Shell::DeleteInstance();
-
-  // Suspend the tear down until all resources are returned via
-  // MojoCompositorFrameSinkClient::ReclaimResources()
-  RunAllPendingInMessageLoop();
   material_design_state_.reset();
   test::MaterialDesignControllerTestAPI::Uninitialize();
   ash_test_environment_->TearDown();
diff --git a/build/config/features.gni b/build/config/features.gni
index 79d317b2..49386f3 100644
--- a/build/config/features.gni
+++ b/build/config/features.gni
@@ -89,8 +89,6 @@
 # Chrome OS: whether to also build the upcoming version of
 # ChromeVox, which can then be enabled via a command-line switch.
 enable_chromevox_next = false
-
-enable_configuration_policy = !is_ios
 #
 # =============================================
 #   PLEASE DO NOT ADD MORE FLAGS TO THIS FILE
diff --git a/build/config/linux/gtk/BUILD.gn b/build/config/linux/gtk/BUILD.gn
index 70110f38..bfe9f584 100644
--- a/build/config/linux/gtk/BUILD.gn
+++ b/build/config/linux/gtk/BUILD.gn
@@ -23,6 +23,7 @@
     "//remoting/host/it2me:remote_assistance_host",
     "//remoting/host:remoting_me2me_host_static",
     "//remoting/test:it2me_standalone_host_main",
+    "//webrtc/examples:peerconnection_client",
   ]
 
   if (use_gtk3) {
diff --git a/chrome/VERSION b/chrome/VERSION
index f5731a6c..287bdcf 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=57
 MINOR=0
-BUILD=2947
+BUILD=2948
 PATCH=0
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 465ff3c..6af9d21c 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1752,6 +1752,15 @@
   <message name="IDS_OOBE_OTHER_KEYBOARD_LAYOUTS" desc="Option group name dividing vendor-configured keyboard layouts from other available layouts in out-of-box 'Select your keyboard' select control.">
     Other keyboards
   </message>
+  <message name="IDS_TIMEZONE_BUTTON_TEXT" desc="Label on a button that opens timezone selection screen" meaning="Label on a button that opens timezone selection screen during initial device configuration">
+    Timezone
+  </message>
+  <message name="IDS_TIMEZONE_SECTION_TITLE" desc="Title of timezone selection screen" meaning="A title of the dialog where user should select device timezone during initial device configuration">
+    Select timezone
+  </message>
+  <message name="IDS_TIMEZONE_DROPDOWN_TITLE" desc="Title of timezone selection dropdown menu" meaning="Small title near timezone dropdown menu suggesting that this is a timezone selector.">
+    Timezone
+  </message>
   <message name="IDS_OOBE_EULA_SECTION_TITLE" desc="Title of Terms of Service screen">
     Google Chrome OS Terms
   </message>
diff --git a/chrome/app/mash/BUILD.gn b/chrome/app/mash/BUILD.gn
index 4ff4877..02bbd48 100644
--- a/chrome/app/mash/BUILD.gn
+++ b/chrome/app/mash/BUILD.gn
@@ -19,10 +19,10 @@
     "//services/service_manager",
     "//services/service_manager/background:lib",
     "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp/standalone_service",
     "//services/service_manager/public/interfaces",
     "//services/service_manager/runner:init",
     "//services/service_manager/runner/common",
+    "//services/service_manager/runner/host:child_process_base",
     "//services/service_manager/runner/host:lib",
     "//services/service_manager/standalone:lib",
     "//url",
diff --git a/chrome/app/mash/mash_runner.cc b/chrome/app/mash/mash_runner.cc
index ddcb260..6f43edb 100644
--- a/chrome/app/mash/mash_runner.cc
+++ b/chrome/app/mash/mash_runner.cc
@@ -32,9 +32,10 @@
 #include "services/service_manager/public/cpp/identity.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context.h"
-#include "services/service_manager/public/cpp/standalone_service/standalone_service.h"
 #include "services/service_manager/public/interfaces/service_factory.mojom.h"
 #include "services/service_manager/runner/common/switches.h"
+#include "services/service_manager/runner/host/child_process.h"
+#include "services/service_manager/runner/host/child_process_base.h"
 #include "services/service_manager/runner/init.h"
 #include "services/service_manager/standalone/context.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -92,11 +93,6 @@
   void AdjustCommandLineArgumentsForTarget(
       const service_manager::Identity& target,
       base::CommandLine* command_line) override {
-    if (target.name() == kChromeMashServiceName ||
-        target.name() == content::mojom::kBrowserServiceName) {
-      command_line->SetProgram(
-          base::CommandLine::ForCurrentProcess()->GetProgram());
-    }
     if (target.name() != content::mojom::kBrowserServiceName) {
       // If running anything other than the browser process, launch a mash
       // child process. The new process will execute MashRunner::RunChild().
@@ -193,9 +189,16 @@
 
   service_manager::WaitForDebuggerIfNecessary();
 
+  base::FilePath path =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+          switches::kChildProcess);
+  if (base::PathExists(path))
+    return service_manager::ChildProcessMain();
+
+  // If the path doesn't exist - try launching this as a packaged service.
   base::i18n::InitializeICU();
   InitializeResources();
-  service_manager::RunStandaloneService(
+  service_manager::ChildProcessMainWithCallback(
       base::Bind(&MashRunner::StartChildApp, base::Unretained(this)));
   return 0;
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a879ebd3..4bad36d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4063,11 +4063,9 @@
     ]
     deps = [
       "//base",
+      "//chrome/browser/policy:path_parser",
       "//chrome/common:constants",
     ]
-    if (enable_configuration_policy) {
-      deps += [ "//chrome/browser/policy:path_parser" ]
-    }
   }
 }
 
@@ -4192,6 +4190,8 @@
     "notifications/notification_test_util.h",
     "notifications/stub_notification_platform_bridge.cc",
     "notifications/stub_notification_platform_bridge.h",
+    "policy/test/local_policy_test_server.cc",
+    "policy/test/local_policy_test_server.h",
     "search_engines/template_url_service_factory_test_util.cc",
     "search_engines/template_url_service_factory_test_util.h",
     "search_engines/template_url_service_test_util.cc",
@@ -4234,6 +4234,7 @@
     "//components/invalidation/impl",
     "//components/invalidation/impl:test_support",
     "//components/password_manager/core/browser:test_support",
+    "//components/policy/core/browser:test_support",
     "//components/prefs:test_support",
     "//components/search_engines:test_support",
     "//components/sessions:test_support",
@@ -4332,14 +4333,6 @@
     deps += [ "//chromeos:test_support" ]
   }
 
-  if (enable_configuration_policy) {
-    sources += [
-      "policy/test/local_policy_test_server.cc",
-      "policy/test/local_policy_test_server.h",
-    ]
-    public_deps += [ "//components/policy/core/browser:test_support" ]
-  }
-
   if (safe_browsing_mode == 1) {
     sources += [
       "extensions/fake_safe_browsing_database_manager.cc",
diff --git a/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc b/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc
index c970666..39d98cc 100644
--- a/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc
+++ b/chrome/browser/media/webrtc/desktop_media_list_ash_unittest.cc
@@ -66,9 +66,6 @@
       .WillRepeatedly(DoDefault());
   list_->StartUpdating(&observer_);
   base::RunLoop().Run();
-
-  // Reset the unique_ptr so the list stops refreshing.
-  list_.reset();
 }
 
 TEST_F(DesktopMediaListAshTest, OneWindow) {
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_network.js b/chrome/browser/resources/chromeos/login/oobe_screen_network.js
index 0b62320..b3cc57d00 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_network.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_network.js
@@ -47,6 +47,12 @@
 
       var inputMethodsList = loadTimeData.getValue('inputMethodsList');
       welcomeScreen.keyboards = inputMethodsList;
+
+      var timezoneList = loadTimeData.getValue('timezoneList');
+      welcomeScreen.timezones = timezoneList;
+
+      welcomeScreen.highlightStrength =
+          loadTimeData.getValue('highlightStrength');
       // -------------------------
 
       this.dropdown_ = $('networks-list');
@@ -183,6 +189,12 @@
 
         welcomeScreen.keyboards = loadTimeData.getValue('inputMethodsList');
         welcomeScreen.enabled = true;
+
+        var timezoneList = loadTimeData.getValue('timezoneList');
+        welcomeScreen.timezones = timezoneList;
+
+        welcomeScreen.highlightStrength =
+            loadTimeData.getValue('highlightStrength');
       }
     },
   };
diff --git a/chrome/browser/resources/chromeos/login/oobe_types.js b/chrome/browser/resources/chromeos/login/oobe_types.js
index e00c022..840e99b6 100644
--- a/chrome/browser/resources/chromeos/login/oobe_types.js
+++ b/chrome/browser/resources/chromeos/login/oobe_types.js
@@ -48,3 +48,19 @@
  * @typedef {!Object}
  */
 OobeTypes.Screen;
+
+/**
+ * Timezone ID.
+ * @typedef {!String}
+ */
+OobeTypes.Timezone;
+
+/**
+ * ChromeOS timezone descriptor.
+ * @typedef {{
+ *   value: (OobeTypes.Timezone|undefined),
+ *   title: (String|undefined),
+ *   selected: (Boolean|undefined),
+ * }}
+ */
+OobeTypes.TimezoneDsc;
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome.css b/chrome/browser/resources/chromeos/login/oobe_welcome.css
index 0e905a4..c89eaccd6 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome.css
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome.css
@@ -36,7 +36,27 @@
   padding: 0 24px;
 }
 
-/************* Accessibility Screen **************/
+/************* Network Selection Screen **************/
 #networkSelectionScreen .bottom-buttons {
   padding: 0 6px; /* = 8px - 2px back button border */
 }
+
+/************* Timezone Screen **************/
+#timezoneScreen .timezone-selection-entry {
+  border-top: 1px solid lightgrey;
+  height: 44px;
+  padding: 0 20px;
+}
+
+#timezoneScreen .timezone-selection-entry:last-of-type {
+  border-bottom: 1px solid lightgrey;
+}
+
+#timezoneScreen .timezone-selection-title {
+  color: rgba(0, 0, 0, 0.87);
+  font: 13px Roboto, sans-serif;
+}
+
+#timezoneScreen .bottom-buttons {
+  padding: 0 24px;
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome.html b/chrome/browser/resources/chromeos/login/oobe_welcome.html
index 6d0ac0b1..e300780 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome.html
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome.html
@@ -40,6 +40,17 @@
     </defs>
   </svg>
 </iron-iconset-svg>
+<iron-iconset-svg name="oobe-welcome-64" size="64">
+  <svg>
+    <defs>
+      <g id="timezone" fill="none" fill-rule="evenodd">
+        <path d="M0 0h64v64H0"></path>
+        <path d="M31.987 5.333C17.253 5.333 5.333 17.267 5.333 32c0 14.733 11.92 26.667 26.654 26.667 14.733 0 26.68-11.934 26.68-26.667 0-14.733-11.947-26.667-26.68-26.667zm.013 48c-11.787 0-21.333-9.546-21.333-21.333 0-11.787 9.546-21.333 21.333-21.333 11.787 0 21.333 9.546 21.333 21.333 0 11.787-9.546 21.333-21.333 21.333z" fill="#5A5A5A"></path>
+        <path fill="#5A5A5A" d="M33.333 18.667h-4v16l13.987 8.4 2.013-3.28-12-7.12"></path>
+      </g>
+    </defs>
+  </svg>
+</iron-iconset-svg>
 
 <dom-module name="oobe-welcome-md">
   <template>
@@ -50,7 +61,10 @@
         current-language="[[currentLanguage]]"
         on-language-button-clicked="onWelcomeSelectLanguageButtonClicked_"
         on-accessibility-button-clicked="onWelcomeAccessibilityButtonClicked_"
-        on-next-button-clicked="onWelcomeNextButtonClicked_">
+        on-timezone-button-clicked="onWelcomeTimezoneButtonClicked_"
+        on-next-button-clicked="onWelcomeNextButtonClicked_"
+        timezone-button-visible="[[isTimezoneButtonVisible_(highlightStrength)]]"
+        >
     </oobe-welcome-dialog>
     <oobe-dialog id="languageScreen" hidden="[[!languageSelectionScreenShown]]"
        has-buttons>
@@ -148,6 +162,29 @@
         </oobe-text-button>
       </div>
     </oobe-dialog>
+    <oobe-dialog id="timezoneScreen" hidden="[[!timezoneScreenShown]]"
+       has-buttons>
+      <iron-icon icon="oobe-welcome-64:timezone" class="oobe-icon"></iron-icon>
+      <div class="header">
+        <h1 class="title" i18n-content="timezoneSectionTitle"></h1>
+      </div>
+      <div class="footer layout vertical">
+        <div class="flex layout horizontal justified timezone-selection-entry">
+          <div class="timezone-selection-title layout vertical center-justified"
+              i18n-content="timezoneDropdownTitle">
+          </div>
+          <oobe-i18n-dropdown id="timezoneSelect" items="[[timezones]]"
+              on-select-item="onTimezoneSelected_"
+              label="$i18n{timezoneDropdownTitle}">
+          </oobe-i18n-dropdown>
+        </div>
+      </div>
+      <div class="bottom-buttons layout horizontal end-justified">
+        <oobe-text-button inverse on-tap="closeTimezoneSection_">
+          <div i18n-content="oobeOKButtonText"></div>
+        </oobe-text-button>
+      </div>
+    </oobe-dialog>
     <oobe-dialog id="networkSelectionScreen"
         hidden="[[!networkSelectionScreenShown]]" has-buttons>
       <iron-icon icon="oobe-welcome:wifi" class="oobe-icon"></iron-icon>
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome.js b/chrome/browser/resources/chromeos/login/oobe_welcome.js
index db32a89..6fb51628 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome.js
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome.js
@@ -59,6 +59,14 @@
     },
 
     /**
+     * Flag that shows Timezone Selection screen.
+     */
+    timezoneScreenShown: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
      * Flag that shows Network Selection screen.
      */
     networkSelectionScreenShown: {
@@ -81,6 +89,23 @@
     a11yStatus: {
       type: Object,
     },
+
+    /**
+     * A list of timezones for Timezone Selection screen.
+     * @type {!Array<OobeTypes.TimezoneDsc>}
+     */
+    timezones: {
+      type: Object,
+      value: [],
+    },
+
+    /**
+     * If UI uses forced keyboard navigation.
+     */
+    highlightStrength: {
+      type: String,
+      value: '',
+    },
   },
 
   /**
@@ -91,6 +116,7 @@
     this.networkSelectionScreenShown = false;
     this.languageSelectionScreenShown = false;
     this.accessibilityOptionsScreenShown = false;
+    this.timezoneScreenShown = false;
   },
 
   /**
@@ -146,6 +172,14 @@
   },
 
   /**
+   * Returns true if timezone button should be visible.
+   * @private
+   */
+  isTimezoneButtonVisible_: function(highlightStrength) {
+    return highlightStrength === 'strong';
+  },
+
+  /**
    * Handle "Next" button for "Welcome" screen.
    *
    * @private
@@ -176,6 +210,16 @@
   },
 
   /**
+   * Handle "Timezone" button for "Welcome" screen.
+   *
+   * @private
+   */
+  onWelcomeTimezoneButtonClicked_: function() {
+    this.hideAllScreens_();
+    this.timezoneScreenShown = true;
+  },
+
+  /**
    * Handle Networwork Setup screen "Proxy settings" button.
    *
    * @private
@@ -345,4 +389,30 @@
     chrome.send(
         event.currentTarget.chromeMessage, [event.currentTarget.checked]);
   },
+
+  /** ******************** Timezone section ******************* */
+
+  /**
+   * Handle "OK" button for "Timezone Selection" screen.
+   *
+   * @private
+   */
+  closeTimezoneSection_: function() {
+    this.hideAllScreens_();
+    this.welcomeScreenShown = true;
+  },
+
+  /**
+   * Handle timezone selection.
+   *
+   * @param {!{detail: {!OobeTypes.Timezone}}} event
+   * @private
+   */
+  onTimezoneSelected_: function(event) {
+    var item = event.detail;
+    if (!item)
+      return;
+
+    this.screen.onTimezoneSelected_(item.value);
+  },
 });
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.html b/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.html
index ed02edb..9cfcd9c 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.html
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.html
@@ -28,6 +28,10 @@
               on-tap="onAccessibilityClicked_">
             <div i18n-content="accessibilityLink"></div>
           </oobe-welcome-secondary-button>
+          <oobe-welcome-secondary-button icon="oobe-welcome-64:timezone"
+              on-tap="onTimezoneClicked_" hidden="[[!timezoneButtonVisible]]">
+            <div i18n-content="timezoneButtonText"></div>
+          </oobe-welcome-secondary-button>
           <div class="flex"></div>
           <oobe-text-button inverse on-tap="onNextClicked_">
             <div i18n-content="welcomeNextButtonText"></div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.js b/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.js
index 177db4d..87cc78a6 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.js
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.js
@@ -12,6 +12,13 @@
       type: String,
       value: '',
     },
+    /**
+     * Controls visibility of "Timezone" button.
+     */
+    timezoneButtonVisible: {
+      type: Boolean,
+      value: false,
+    },
   },
 
   onLanguageClicked_: function() {
@@ -22,6 +29,10 @@
     this.fire('accessibility-button-clicked');
   },
 
+  onTimezoneClicked_: function() {
+    this.fire('timezone-button-clicked');
+  },
+
   onNextClicked_: function() {
     this.fire('next-button-clicked');
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
index a9f516d0..c428fa09 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
@@ -166,6 +166,7 @@
   builder->Add("languageSectionTitle", IDS_LANGUAGE_SECTION_TITLE);
   builder->Add("accessibilitySectionTitle", IDS_ACCESSIBILITY_SECTION_TITLE);
   builder->Add("accessibilitySectionHint", IDS_ACCESSIBILITY_SECTION_HINT);
+  builder->Add("timezoneSectionTitle", IDS_TIMEZONE_SECTION_TITLE);
   builder->Add("networkSectionTitle", IDS_NETWORK_SECTION_TITLE);
   builder->Add("networkSectionHint", IDS_NETWORK_SECTION_HINT);
 
@@ -185,6 +186,9 @@
   builder->Add("spokenFeedbackOptionOn", IDS_SPOKEN_FEEDBACK_OPTION_ON);
   builder->Add("virtualKeyboardOptionOff", IDS_VIRTUAL_KEYBOARD_OPTION_OFF);
   builder->Add("virtualKeyboardOptionOn", IDS_VIRTUAL_KEYBOARD_OPTION_ON);
+
+  builder->Add("timezoneDropdownTitle", IDS_TIMEZONE_DROPDOWN_TITLE);
+  builder->Add("timezoneButtonText", IDS_TIMEZONE_BUTTON_TEXT);
 }
 
 void NetworkScreenHandler::GetAdditionalParameters(
diff --git a/chrome/browser/win/jumplist.cc b/chrome/browser/win/jumplist.cc
index 1b9d92e..1d60b6b0 100644
--- a/chrome/browser/win/jumplist.cc
+++ b/chrome/browser/win/jumplist.cc
@@ -4,9 +4,12 @@
 
 #include "chrome/browser/win/jumplist.h"
 
+#include <windows.h>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
+#include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
@@ -59,11 +62,13 @@
 // JumpList folder's move operation status.
 enum MoveOperationResult {
   SUCCESS = 0,
-  MAX_PATH_CHECK_FAILED = 1 << 0,
-  MOVE_FILE_EX_FAILED = 1 << 1,
-  COPY_AND_DELETE_DIR_FAILED = 1 << 2,
-  DELETE_FAILED = 1 << 3,
-  END = 1 << 4
+  MOVE_FILE_EX_FAILED = 1 << 0,
+  COPY_DIR_FAILED = 1 << 1,
+  DELETE_FILE_RECURSIVE_FAILED = 1 << 2,
+  DELETE_SOURCE_FOLDER_FAILED = 1 << 3,
+  DELETE_TARGET_FOLDER_FAILED = 1 << 4,
+  DELETE_DIR_READ_ONLY = 1 << 5,
+  END = 1 << 6
 };
 
 // Append the common switches to each shell link.
@@ -225,10 +230,202 @@
 
 // A wrapper function for recording UMA histogram.
 void RecordFolderMoveResult(int move_operation_status) {
-  UMA_HISTOGRAM_ENUMERATION("WinJumplist.FolderMoveResults",
+  UMA_HISTOGRAM_ENUMERATION("WinJumplist.DetailedFolderMoveResults",
                             move_operation_status, MoveOperationResult::END);
 }
 
+// This function is an exact copy of //base for UMA debugging purpose of
+// DeleteFile() below.
+// Deletes all files and directories in a path.
+// Returns false on the first failure it encounters.
+bool DeleteFileRecursive(const base::FilePath& path,
+                         const base::FilePath::StringType& pattern,
+                         bool recursive) {
+  base::FileEnumerator traversal(
+      path, false,
+      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES, pattern);
+  for (base::FilePath current = traversal.Next(); !current.empty();
+       current = traversal.Next()) {
+    // Try to clear the read-only bit if we find it.
+    base::FileEnumerator::FileInfo info = traversal.GetInfo();
+    if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) &&
+        (recursive || !info.IsDirectory())) {
+      ::SetFileAttributes(
+          current.value().c_str(),
+          info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
+    }
+
+    if (info.IsDirectory()) {
+      if (recursive && (!DeleteFileRecursive(current, pattern, true) ||
+                        !::RemoveDirectory(current.value().c_str())))
+        return false;
+    } else if (!::DeleteFile(current.value().c_str())) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// This function is a copy from //base for UMA debugging purpose.
+// Deletes all files and directories in a path.
+// Returns false on the first failure it encounters.
+bool DeleteFile(const base::FilePath& path,
+                bool recursive,
+                int* move_operation_status) {
+  base::ThreadRestrictions::AssertIOAllowed();
+
+  if (path.empty())
+    return true;
+
+  if (path.value().length() >= MAX_PATH)
+    return false;
+
+  // Handle any path with wildcards.
+  if (path.BaseName().value().find_first_of(L"*?") !=
+      base::FilePath::StringType::npos) {
+    bool ret =
+        DeleteFileRecursive(path.DirName(), path.BaseName().value(), recursive);
+    if (!ret) {
+      *move_operation_status |=
+          MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED;
+      *move_operation_status |=
+          MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED;
+    }
+    return ret;
+  }
+  DWORD attr = ::GetFileAttributes(path.value().c_str());
+  // We're done if we can't find the path.
+  if (attr == INVALID_FILE_ATTRIBUTES)
+    return true;
+  // We may need to clear the read-only bit.
+  if ((attr & FILE_ATTRIBUTE_READONLY) &&
+      !::SetFileAttributes(path.value().c_str(),
+                           attr & ~FILE_ATTRIBUTE_READONLY)) {
+    *move_operation_status |= MoveOperationResult::DELETE_DIR_READ_ONLY;
+    *move_operation_status |= MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED;
+    *move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED;
+    return false;
+  }
+  // Directories are handled differently if they're recursive.
+  if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
+    return !!::DeleteFile(path.value().c_str());
+  // Handle a simple, single file delete.
+  if (!recursive || DeleteFileRecursive(path, L"*", true)) {
+    bool ret = !!::RemoveDirectory(path.value().c_str());
+    if (!ret)
+      *move_operation_status |=
+          MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED;
+    return ret;
+  }
+
+  *move_operation_status |= MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED;
+  *move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED;
+  return false;
+}
+
+// This function is mostly copied from //base.
+// It has some issue when the dest dir string contains the src dir string.
+// Temporary fix by replacing the old logic with IsParent() call.
+bool CopyDirectoryTemp(const base::FilePath& from_path,
+                       const base::FilePath& to_path,
+                       bool recursive) {
+  // NOTE(maruel): Previous version of this function used to call
+  // SHFileOperation().  This used to copy the file attributes and extended
+  // attributes, OLE structured storage, NTFS file system alternate data
+  // streams, SECURITY_DESCRIPTOR. In practice, this is not what we want, we
+  // want the containing directory to propagate its SECURITY_DESCRIPTOR.
+  base::ThreadRestrictions::AssertIOAllowed();
+
+  // NOTE: I suspect we could support longer paths, but that would involve
+  // analyzing all our usage of files.
+  if (from_path.value().length() >= MAX_PATH ||
+      to_path.value().length() >= MAX_PATH) {
+    return false;
+  }
+
+  // This function does not properly handle destinations within the source.
+  base::FilePath real_to_path = to_path;
+  if (base::PathExists(real_to_path)) {
+    real_to_path = base::MakeAbsoluteFilePath(real_to_path);
+    if (real_to_path.empty())
+      return false;
+  } else {
+    real_to_path = base::MakeAbsoluteFilePath(real_to_path.DirName());
+    if (real_to_path.empty())
+      return false;
+  }
+  base::FilePath real_from_path = base::MakeAbsoluteFilePath(from_path);
+  if (real_from_path.empty())
+    return false;
+
+  // Originally this CopyDirectory function returns false when to_path string
+  // contains from_path string. While this can be that the to_path is within
+  // the from_path, e.g., parent-child relationship in terms of folder, it
+  // also can be the two paths are in the same folder, just that one name
+  // contains
+  // the other, e.g. C:\\JumpListIcon and C:\\JumpListIconOld.
+  // We make this function not return false in the latter situation by calling
+  // IsParent() function.
+  if (real_to_path.value().size() >= real_from_path.value().size() &&
+      real_from_path.IsParent(real_to_path)) {
+    return false;
+  }
+
+  int traverse_type = base::FileEnumerator::FILES;
+  if (recursive)
+    traverse_type |= base::FileEnumerator::DIRECTORIES;
+  base::FileEnumerator traversal(from_path, recursive, traverse_type);
+
+  if (!base::PathExists(from_path)) {
+    DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
+                << from_path.value().c_str();
+    return false;
+  }
+  // TODO(maruel): This is not necessary anymore.
+  DCHECK(recursive || base::DirectoryExists(from_path));
+
+  base::FilePath current = from_path;
+  bool from_is_dir = base::DirectoryExists(from_path);
+  bool success = true;
+  base::FilePath from_path_base = from_path;
+  if (recursive && base::DirectoryExists(to_path)) {
+    // If the destination already exists and is a directory, then the
+    // top level of source needs to be copied.
+    from_path_base = from_path.DirName();
+  }
+
+  while (success && !current.empty()) {
+    // current is the source path, including from_path, so append
+    // the suffix after from_path to to_path to create the target_path.
+    base::FilePath target_path(to_path);
+    if (from_path_base != current) {
+      if (!from_path_base.AppendRelativePath(current, &target_path)) {
+        success = false;
+        break;
+      }
+    }
+
+    if (from_is_dir) {
+      if (!base::DirectoryExists(target_path) &&
+          !::CreateDirectory(target_path.value().c_str(), NULL)) {
+        DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
+                    << target_path.value().c_str();
+        success = false;
+      }
+    } else if (!base::CopyFile(current, target_path)) {
+      DLOG(ERROR) << "CopyDirectory() couldn't create file: "
+                  << target_path.value().c_str();
+      success = false;
+    }
+
+    current = traversal.Next();
+    if (!current.empty())
+      from_is_dir = traversal.GetInfo().IsDirectory();
+  }
+
+  return success;
+}
+
 // This function is a temporary fork of Move() and MoveUnsafe() in
 // base/files/file_util.h, where UMA functions are added to gather user data.
 // As //base is highly mature, we tend not to add this temporary function
@@ -244,14 +441,12 @@
   // This variable records the status of move operations.
   int move_operation_status = MoveOperationResult::SUCCESS;
   if (folder_delete_fail)
-    move_operation_status |= MoveOperationResult::DELETE_FAILED;
+    move_operation_status |= MoveOperationResult::DELETE_TARGET_FOLDER_FAILED;
 
   // NOTE: I suspect we could support longer paths, but that would involve
   // analyzing all our usage of files.
   if (from_path.value().length() >= MAX_PATH ||
       to_path.value().length() >= MAX_PATH) {
-    move_operation_status |= MoveOperationResult::MAX_PATH_CHECK_FAILED;
-    RecordFolderMoveResult(move_operation_status);
     return false;
   }
   if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
@@ -271,14 +466,21 @@
     // MoveFileEx fails if moving directory across volumes. We will simulate
     // the move by using Copy and Delete. Ideally we could check whether
     // from_path and to_path are indeed in different volumes.
-    ret = base::internal::CopyAndDeleteDirectory(from_path, to_path);
+    if (CopyDirectoryTemp(from_path, to_path, true)) {
+      if (DeleteFile(from_path, true, &move_operation_status))
+        ret = true;
+    } else {
+      move_operation_status |= MoveOperationResult::COPY_DIR_FAILED;
+      move_operation_status |=
+          MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED;
+      move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED;
+    }
   }
 
   if (!ret) {
     // Leave a clue about what went wrong so that it can be (at least) picked
     // up by a PLOG entry.
     ::SetLastError(last_error);
-    move_operation_status |= MoveOperationResult::COPY_AND_DELETE_DIR_FAILED;
   }
 
   RecordFolderMoveResult(move_operation_status);
diff --git a/chrome/test/base/mash_browser_tests_main.cc b/chrome/test/base/mash_browser_tests_main.cc
index 324f992..daea62c6 100644
--- a/chrome/test/base/mash_browser_tests_main.cc
+++ b/chrome/test/base/mash_browser_tests_main.cc
@@ -24,8 +24,9 @@
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/service_manager/public/cpp/service_runner.h"
-#include "services/service_manager/public/cpp/standalone_service/standalone_service.h"
 #include "services/service_manager/runner/common/switches.h"
+#include "services/service_manager/runner/host/child_process.h"
+#include "services/service_manager/runner/host/child_process_base.h"
 #include "services/service_manager/runner/init.h"
 
 namespace {
@@ -149,11 +150,23 @@
     base::AtExitManager exit_manager;
 #endif
     base::i18n::InitializeICU();
-    service_manager::RunStandaloneService(base::Bind(&StartChildApp));
+    service_manager::ChildProcessMainWithCallback(base::Bind(&StartChildApp));
     *exit_code = 0;
     return true;
   }
 
+  if (command_line.HasSwitch(switches::kChildProcess) &&
+      !command_line.HasSwitch(MojoTestConnector::kTestSwitch)) {
+    base::AtExitManager at_exit;
+    service_manager::InitializeLogging();
+    service_manager::WaitForDebuggerIfNecessary();
+#if !defined(OFFICIAL_BUILD) && defined(OS_WIN)
+    base::RouteStdioToConsole(false);
+#endif
+    *exit_code = service_manager::ChildProcessMain();
+    return true;
+  }
+
   int default_jobs = std::max(1, base::SysInfo::NumberOfProcessors() / 2);
   MashTestLauncherDelegate delegate;
   // --single_process and no primoridal pipe token indicate we were run directly
diff --git a/chrome/test/base/mojo_test_connector.cc b/chrome/test/base/mojo_test_connector.cc
index 3334cb5..0e41bc307f 100644
--- a/chrome/test/base/mojo_test_connector.cc
+++ b/chrome/test/base/mojo_test_connector.cc
@@ -214,11 +214,8 @@
       const service_manager::Identity& target,
       base::CommandLine* command_line) override {
     if (target.name() != kTestName) {
-      if (target.name() == kTestRunnerName) {
+      if (target.name() == kTestRunnerName)
         RemoveMashFromBrowserTests(command_line);
-        command_line->SetProgram(
-            base::CommandLine::ForCurrentProcess()->GetProgram());
-      }
       command_line->AppendSwitch(MojoTestConnector::kMashApp);
       return;
     }
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 1948987..d362ecc 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -198,6 +198,8 @@
       "//components/offline_pages/content/background_loader:unit_tests",
       "//components/packed_ct_ev_whitelist:unit_tests",
       "//components/password_manager/content/browser:unit_tests",
+      "//components/policy/core/browser:unit_tests",
+      "//components/policy/core/common:unit_tests",
       "//components/precache/content:unit_tests",
       "//components/safe_browsing_db:unit_tests",
       "//components/safe_json:unit_tests",
@@ -236,6 +238,7 @@
       "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
       "//components/invalidation/impl",
       "//components/invalidation/impl:java",
+      "//components/policy/android:policy_java",
       "//components/safe_json",
       "//components/safe_json/android:safe_json_java",
       "//components/signin/core/browser",
@@ -253,10 +256,6 @@
       "//ui/gfx",
       "//v8:v8_external_startup_data_assets",
     ]
-
-    if (enable_configuration_policy) {
-      deps += [ "//components/policy/android:policy_java" ]
-    }
   }
 
   # Desktop-only deps.
@@ -285,13 +284,6 @@
     ]
   }
 
-  if (enable_configuration_policy) {
-    deps += [
-      "//components/policy/core/browser:unit_tests",
-      "//components/policy/core/common:unit_tests",
-    ]
-  }
-
   if (toolkit_views) {
     deps += [ "//components/constrained_window:unit_tests" ]
   }
diff --git a/components/error_page/common/localized_error.cc b/components/error_page/common/localized_error.cc
index e81da54..462054a53 100644
--- a/components/error_page/common/localized_error.cc
+++ b/components/error_page/common/localized_error.cc
@@ -970,7 +970,13 @@
 
   // Add the reload suggestion, if needed for pages that didn't come
   // from a post.
+#if defined(OS_ANDROID)
+  bool reload_visible = false;
+#endif  // defined(OS_ANDROID)
   if (params->suggest_reload && !is_post) {
+#if defined(OS_ANDROID)
+    reload_visible = true;
+#endif  // defined(OS_ANDROID)
     base::DictionaryValue* reload_button = new base::DictionaryValue;
     reload_button->SetString(
         "msg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_RELOAD));
@@ -1023,8 +1029,8 @@
   }
 
 #if defined(OS_ANDROID)
-  if (!show_saved_copy_visible && !is_incognito &&
-      failed_url.SchemeIsHTTPOrHTTPS() &&
+  if (!reload_visible && !show_saved_copy_visible && !is_incognito &&
+      failed_url.is_valid() && failed_url.SchemeIsHTTPOrHTTPS() &&
       offline_pages::IsOfflinePagesAsyncDownloadEnabled()) {
     std::unique_ptr<base::DictionaryValue> download_button =
         base::MakeUnique<base::DictionaryValue>();
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index 09758654..d4aebaf 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -9,10 +9,6 @@
   sources = [
     "buffer.cc",
     "buffer.h",
-    "compositor_frame_sink.cc",
-    "compositor_frame_sink.h",
-    "compositor_frame_sink_holder.cc",
-    "compositor_frame_sink_holder.h",
     "display.cc",
     "display.h",
     "gamepad.cc",
@@ -20,6 +16,7 @@
     "keyboard.cc",
     "keyboard.h",
     "keyboard_delegate.h",
+    "keyboard_device_configuration_delegate.h",
     "notification_surface.cc",
     "notification_surface.h",
     "notification_surface_manager.h",
@@ -62,6 +59,7 @@
     "//ui/aura",
     "//ui/compositor",
     "//ui/display/manager",
+    "//ui/events/devices:devices",
     "//ui/gfx",
     "//ui/gfx/geometry",
     "//ui/gl",
@@ -158,7 +156,6 @@
     "//base",
     "//base/test:test_support",
     "//device/gamepad:test_helpers",
-    "//mojo/edk/embedder:headers",
   ]
 
   data_deps = [
diff --git a/components/exo/DEPS b/components/exo/DEPS
index 17b1c94c..6eb071a 100644
--- a/components/exo/DEPS
+++ b/components/exo/DEPS
@@ -4,15 +4,8 @@
   "+chromeos/audio/chromeos_sounds.h",
   "+device/gamepad",
   "+gpu",
-  "+mojo/public/cpp",
   "+services/ui/public/cpp",
   "+third_party/khronos",
   "+third_party/skia",
   "+ui",
 ]
-
-specific_include_rules = {
-  "run_all_unittests\.cc": [
-    "+mojo/edk/embedder/embedder.h",
-  ],
-}
diff --git a/components/exo/compositor_frame_sink.cc b/components/exo/compositor_frame_sink.cc
deleted file mode 100644
index 9d4ab95..0000000
--- a/components/exo/compositor_frame_sink.cc
+++ /dev/null
@@ -1,101 +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 "components/exo/compositor_frame_sink.h"
-
-#include "cc/surfaces/surface.h"
-#include "cc/surfaces/surface_manager.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace exo {
-
-////////////////////////////////////////////////////////////////////////////////
-// ExoComopositorFrameSink, public:
-
-// static
-void CompositorFrameSink::Create(
-    const cc::FrameSinkId& frame_sink_id,
-    cc::SurfaceManager* surface_manager,
-    cc::mojom::MojoCompositorFrameSinkClientPtr client,
-    cc::mojom::MojoCompositorFrameSinkRequest request) {
-  std::unique_ptr<CompositorFrameSink> impl =
-      base::MakeUnique<CompositorFrameSink>(frame_sink_id, surface_manager,
-                                            std::move(client));
-  CompositorFrameSink* compositor_frame_sink = impl.get();
-  compositor_frame_sink->binding_ =
-      mojo::MakeStrongBinding(std::move(impl), std::move(request));
-}
-
-CompositorFrameSink::CompositorFrameSink(
-    const cc::FrameSinkId& frame_sink_id,
-    cc::SurfaceManager* surface_manager,
-    cc::mojom::MojoCompositorFrameSinkClientPtr client)
-    : support_(this, surface_manager, frame_sink_id, nullptr),
-      client_(std::move(client)) {}
-
-CompositorFrameSink::~CompositorFrameSink() {}
-
-////////////////////////////////////////////////////////////////////////////////
-// cc::mojom::MojoCompositorFrameSink overrides:
-
-void CompositorFrameSink::SetNeedsBeginFrame(bool needs_begin_frame) {
-  support_.SetNeedsBeginFrame(needs_begin_frame);
-}
-
-void CompositorFrameSink::SubmitCompositorFrame(
-    const cc::LocalFrameId& local_frame_id,
-    cc::CompositorFrame frame) {
-  support_.SubmitCompositorFrame(local_frame_id, std::move(frame));
-}
-
-void CompositorFrameSink::AddSurfaceReferences(
-    const std::vector<cc::SurfaceReference>& references) {
-  // TODO(fsamuel, staraz): Implement this.
-  NOTIMPLEMENTED();
-}
-
-void CompositorFrameSink::RemoveSurfaceReferences(
-    const std::vector<cc::SurfaceReference>& references) {
-  // TODO(fsamuel, staraz): Implement this.
-  NOTIMPLEMENTED();
-}
-
-void CompositorFrameSink::EvictFrame() {
-  support_.EvictFrame();
-}
-
-void CompositorFrameSink::Require(const cc::LocalFrameId& local_frame_id,
-                                  const cc::SurfaceSequence& sequence) {
-  support_.Require(local_frame_id, sequence);
-}
-
-void CompositorFrameSink::Satisfy(const cc::SurfaceSequence& sequence) {
-  support_.Satisfy(sequence);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// cc::CompositorFrameSinkSupportClient overrides:
-
-void CompositorFrameSink::DidReceiveCompositorFrameAck() {
-  if (client_)
-    client_->DidReceiveCompositorFrameAck();
-}
-
-void CompositorFrameSink::OnBeginFrame(const cc::BeginFrameArgs& args) {
-  if (client_)
-    client_->OnBeginFrame(args);
-}
-
-void CompositorFrameSink::ReclaimResources(
-    const cc::ReturnedResourceArray& resources) {
-  if (client_)
-    client_->ReclaimResources(resources);
-}
-
-void CompositorFrameSink::WillDrawSurface() {
-  if (client_)
-    client_->WillDrawSurface();
-}
-
-}  // namespace exo
diff --git a/components/exo/compositor_frame_sink.h b/components/exo/compositor_frame_sink.h
deleted file mode 100644
index 4eace4b..0000000
--- a/components/exo/compositor_frame_sink.h
+++ /dev/null
@@ -1,61 +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 COMPONENTS_EXO_EXO_COMPOSITOR_FRAME_SINK_H_
-#define COMPONENTS_EXO_EXO_COMPOSITOR_FRAME_SINK_H_
-
-#include "cc/ipc/compositor_frame.mojom.h"
-#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
-#include "cc/resources/transferable_resource.h"
-#include "cc/surfaces/compositor_frame_sink_support.h"
-#include "cc/surfaces/compositor_frame_sink_support_client.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace exo {
-
-class CompositorFrameSink : public cc::CompositorFrameSinkSupportClient,
-                            public cc::mojom::MojoCompositorFrameSink {
- public:
-  static void Create(const cc::FrameSinkId& frame_sink_id,
-                     cc::SurfaceManager* surface_manager,
-                     cc::mojom::MojoCompositorFrameSinkClientPtr client,
-                     cc::mojom::MojoCompositorFrameSinkRequest request);
-
-  CompositorFrameSink(const cc::FrameSinkId& frame_sink_id,
-                      cc::SurfaceManager* surface_manager,
-                      cc::mojom::MojoCompositorFrameSinkClientPtr client);
-
-  ~CompositorFrameSink() override;
-
-  // Overridden from cc::mojom::MojoCompositorFrameSink:
-  void SetNeedsBeginFrame(bool needs_begin_frame) override;
-  void SubmitCompositorFrame(const cc::LocalFrameId& local_frame_id,
-                             cc::CompositorFrame frame) override;
-  void AddSurfaceReferences(
-      const std::vector<cc::SurfaceReference>& references) override;
-  void RemoveSurfaceReferences(
-      const std::vector<cc::SurfaceReference>& references) override;
-  void EvictFrame() override;
-  void Require(const cc::LocalFrameId& local_frame_id,
-               const cc::SurfaceSequence& sequence) override;
-  void Satisfy(const cc::SurfaceSequence& sequence) override;
-
-  // Overridden from cc::CompositorFrameSinkSupportClient:
-  void DidReceiveCompositorFrameAck() override;
-  void OnBeginFrame(const cc::BeginFrameArgs& args) override;
-  void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
-  void WillDrawSurface() override;
-
- private:
-  cc::CompositorFrameSinkSupport support_;
-  cc::mojom::MojoCompositorFrameSinkClientPtr client_;
-  cc::ReturnedResourceArray surface_returned_resources_;
-  mojo::StrongBindingPtr<cc::mojom::MojoCompositorFrameSink> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(CompositorFrameSink);
-};
-
-}  // namespace exo
-
-#endif  // COMPONENTS_EXO_EXO_COMPOSITOR_FRAME_SINK_H_
diff --git a/components/exo/compositor_frame_sink_holder.cc b/components/exo/compositor_frame_sink_holder.cc
deleted file mode 100644
index dd82aff..0000000
--- a/components/exo/compositor_frame_sink_holder.cc
+++ /dev/null
@@ -1,142 +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 "components/exo/compositor_frame_sink_holder.h"
-
-#include "cc/resources/returned_resource.h"
-#include "components/exo/surface.h"
-
-namespace exo {
-
-////////////////////////////////////////////////////////////////////////////////
-// CompositorFrameSinkHolder, public:
-
-CompositorFrameSinkHolder::CompositorFrameSinkHolder(
-    Surface* surface,
-    std::unique_ptr<CompositorFrameSink> frame_sink,
-    cc::mojom::MojoCompositorFrameSinkClientRequest request)
-    : surface_(surface),
-      frame_sink_(std::move(frame_sink)),
-      begin_frame_source_(base::MakeUnique<cc::ExternalBeginFrameSource>(this)),
-      binding_(this, std::move(request)),
-      weak_factory_(this) {
-  surface_->AddSurfaceObserver(this);
-}
-
-bool CompositorFrameSinkHolder::HasReleaseCallbackForResource(
-    cc::ResourceId id) {
-  return release_callbacks_.find(id) != release_callbacks_.end();
-}
-
-void CompositorFrameSinkHolder::AddResourceReleaseCallback(
-    cc::ResourceId id,
-    std::unique_ptr<cc::SingleReleaseCallback> callback) {
-  release_callbacks_[id] = std::make_pair(this, std::move(callback));
-}
-
-void CompositorFrameSinkHolder::ActivateFrameCallbacks(
-    std::list<FrameCallback>* frame_callbacks) {
-  active_frame_callbacks_.splice(active_frame_callbacks_.end(),
-                                 *frame_callbacks);
-  UpdateNeedsBeginFrame();
-}
-
-void CompositorFrameSinkHolder::CancelFrameCallbacks() {
-  // Call pending frame callbacks with a null frame time to indicate that they
-  // have been cancelled.
-  for (const auto& frame_callback : active_frame_callbacks_)
-    frame_callback.Run(base::TimeTicks());
-}
-
-void CompositorFrameSinkHolder::SetNeedsBeginFrame(bool needs_begin_frame) {
-  needs_begin_frame_ = needs_begin_frame;
-  OnNeedsBeginFrames(needs_begin_frame);
-}
-
-void CompositorFrameSinkHolder::Satisfy(const cc::SurfaceSequence& sequence) {
-  frame_sink_->Satisfy(sequence);
-}
-
-void CompositorFrameSinkHolder::Require(const cc::SurfaceId& id,
-                                        const cc::SurfaceSequence& sequence) {
-  frame_sink_->Require(id.local_frame_id(), sequence);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// cc::mojom::MojoCompositorFrameSinkClient overrides:
-
-void CompositorFrameSinkHolder::DidReceiveCompositorFrameAck() {
-  // TODO(staraz): Implement this
-}
-
-void CompositorFrameSinkHolder::OnBeginFrame(const cc::BeginFrameArgs& args) {
-  while (!active_frame_callbacks_.empty()) {
-    active_frame_callbacks_.front().Run(args.frame_time);
-    active_frame_callbacks_.pop_front();
-  }
-  begin_frame_source_->OnBeginFrame(args);
-}
-
-void CompositorFrameSinkHolder::ReclaimResources(
-    const cc::ReturnedResourceArray& resources) {
-  for (auto& resource : resources) {
-    auto it = release_callbacks_.find(resource.id);
-    DCHECK(it != release_callbacks_.end());
-    std::unique_ptr<cc::SingleReleaseCallback> callback =
-        std::move(it->second.second);
-    release_callbacks_.erase(it);
-    callback->Run(resource.sync_token, resource.lost);
-  }
-}
-
-void CompositorFrameSinkHolder::WillDrawSurface() {
-  if (surface_)
-    surface_->WillDraw();
-
-  UpdateNeedsBeginFrame();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// cc::BeginFrameObserver overrides:
-
-const cc::BeginFrameArgs& CompositorFrameSinkHolder::LastUsedBeginFrameArgs()
-    const {
-  return last_begin_frame_args_;
-}
-
-void CompositorFrameSinkHolder::OnBeginFrameSourcePausedChanged(bool paused) {}
-
-////////////////////////////////////////////////////////////////////////////////
-// cc::ExternalBeginFrameSouceClient overrides:
-
-void CompositorFrameSinkHolder::OnNeedsBeginFrames(bool needs_begin_frames) {
-  frame_sink_->SetNeedsBeginFrame(needs_begin_frames);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// SurfaceObserver overrides:
-
-void CompositorFrameSinkHolder::OnSurfaceDestroying(Surface* surface) {
-  surface_->RemoveSurfaceObserver(this);
-  surface_ = nullptr;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ExoComopositorFrameSink, private:
-
-CompositorFrameSinkHolder::~CompositorFrameSinkHolder() {}
-
-void CompositorFrameSinkHolder::UpdateNeedsBeginFrame() {
-  if (!begin_frame_source_)
-    return;
-
-  bool needs_begin_frame = !active_frame_callbacks_.empty();
-  if (needs_begin_frame == needs_begin_frame_)
-    return;
-
-  needs_begin_frame_ = needs_begin_frame;
-  OnNeedsBeginFrames(needs_begin_frame_);
-}
-
-}  // namespace exo
diff --git a/components/exo/compositor_frame_sink_holder.h b/components/exo/compositor_frame_sink_holder.h
deleted file mode 100644
index 59cc987..0000000
--- a/components/exo/compositor_frame_sink_holder.h
+++ /dev/null
@@ -1,108 +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 COMPONENTS_EXO_COMPOSITOR_FRAME_SINK_HOLDER_H_
-#define COMPONENTS_EXO_COMPOSITOR_FRAME_SINK_HOLDER_H_
-
-#include <list>
-#include <map>
-#include <memory>
-
-#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
-#include "cc/resources/single_release_callback.h"
-#include "cc/resources/transferable_resource.h"
-#include "cc/scheduler/begin_frame_source.h"
-#include "components/exo/compositor_frame_sink.h"
-#include "components/exo/surface_observer.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace exo {
-class Surface;
-
-// This class talks to MojoCompositorFrameSink and keeps track of references to
-// the contents of Buffers. It's keeped alive by references from
-// release_callbacks_. It's destroyed when its owning Surface is destroyed and
-// the last outstanding release callback is called.
-class CompositorFrameSinkHolder
-    : public base::RefCounted<CompositorFrameSinkHolder>,
-      public cc::ExternalBeginFrameSourceClient,
-      public cc::mojom::MojoCompositorFrameSinkClient,
-      public cc::BeginFrameObserver,
-      public SurfaceObserver {
- public:
-  CompositorFrameSinkHolder(
-      Surface* surface,
-      std::unique_ptr<CompositorFrameSink> frame_sink,
-      cc::mojom::MojoCompositorFrameSinkClientRequest request);
-
-  bool HasReleaseCallbackForResource(cc::ResourceId id);
-  void AddResourceReleaseCallback(
-      cc::ResourceId id,
-      std::unique_ptr<cc::SingleReleaseCallback> callback);
-
-  CompositorFrameSink* GetCompositorFrameSink() { return frame_sink_.get(); }
-
-  base::WeakPtr<CompositorFrameSinkHolder> GetWeakPtr() {
-    return weak_factory_.GetWeakPtr();
-  }
-
-  using FrameCallback = base::Callback<void(base::TimeTicks frame_time)>;
-  void ActivateFrameCallbacks(std::list<FrameCallback>* frame_callbacks);
-  void CancelFrameCallbacks();
-
-  void SetNeedsBeginFrame(bool needs_begin_frame);
-
-  void Satisfy(const cc::SurfaceSequence& sequence);
-  void Require(const cc::SurfaceId& id, const cc::SurfaceSequence& sequence);
-
-  // Overridden from cc::mojom::MojoCompositorFrameSinkClient:
-  void DidReceiveCompositorFrameAck() override;
-  void OnBeginFrame(const cc::BeginFrameArgs& args) override;
-  void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
-  void WillDrawSurface() override;
-
-  // Overridden from cc::BeginFrameObserver:
-  const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
-  void OnBeginFrameSourcePausedChanged(bool paused) override;
-
-  // Overridden from cc::ExternalBeginFrameSouceClient:
-  void OnNeedsBeginFrames(bool needs_begin_frames) override;
-
-  // Overridden from SurfaceObserver:
-  void OnSurfaceDestroying(Surface* surface) override;
-
- private:
-  friend class base::RefCounted<CompositorFrameSinkHolder>;
-
-  ~CompositorFrameSinkHolder() override;
-
-  void UpdateNeedsBeginFrame();
-
-  // Each release callback holds a reference to the CompositorFrameSinkHolder
-  // itself to keep it alive. Running and erasing the callbacks might result in
-  // the instance being destroyed. Therefore, we should not access any member
-  // variables after running and erasing the callbacks.
-  using ResourceReleaseCallbackMap =
-      std::map<int,
-               std::pair<scoped_refptr<CompositorFrameSinkHolder>,
-                         std::unique_ptr<cc::SingleReleaseCallback>>>;
-  ResourceReleaseCallbackMap release_callbacks_;
-
-  Surface* surface_;
-  std::unique_ptr<CompositorFrameSink> frame_sink_;
-
-  std::list<FrameCallback> active_frame_callbacks_;
-  std::unique_ptr<cc::ExternalBeginFrameSource> begin_frame_source_;
-  bool needs_begin_frame_ = false;
-  cc::BeginFrameArgs last_begin_frame_args_;
-  mojo::Binding<cc::mojom::MojoCompositorFrameSinkClient> binding_;
-
-  base::WeakPtrFactory<CompositorFrameSinkHolder> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkHolder);
-};
-
-}  // namespace exo
-
-#endif  // COMPONENTS_EXO_COMPOSITOR_FRAME_SINK_HOLDER_H_
diff --git a/components/exo/keyboard.cc b/components/exo/keyboard.cc
index 2ac2454..206b61f7 100644
--- a/components/exo/keyboard.cc
+++ b/components/exo/keyboard.cc
@@ -5,16 +5,20 @@
 #include "components/exo/keyboard.h"
 
 #include "components/exo/keyboard_delegate.h"
+#include "components/exo/keyboard_device_configuration_delegate.h"
 #include "components/exo/shell_surface.h"
 #include "components/exo/surface.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/window.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/events/base_event_utils.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/input_device_manager.h"
 #include "ui/events/event.h"
 #include "ui/views/widget/widget.h"
 
 namespace exo {
+namespace {
 
 bool ConsumedByIme(Surface* focus, const ui::KeyEvent* event) {
   // Check if IME consumed the event, to avoid it to be doubly processed.
@@ -70,6 +74,21 @@
   return false;
 }
 
+bool IsPhysicalKeyboardEnabled() {
+  // The internal keyboard is enabled if maximize mode is not enabled.
+  if (WMHelper::GetInstance()->IsMaximizeModeWindowManagerEnabled())
+    return true;
+
+  for (auto& keyboard :
+       ui::InputDeviceManager::GetInstance()->GetKeyboardDevices()) {
+    if (keyboard.type != ui::InputDeviceType::INPUT_DEVICE_INTERNAL)
+      return true;
+  }
+  return false;
+}
+
+}  // namespace
+
 ////////////////////////////////////////////////////////////////////////////////
 // Keyboard, public:
 
@@ -77,16 +96,28 @@
   auto* helper = WMHelper::GetInstance();
   helper->AddPostTargetHandler(this);
   helper->AddFocusObserver(this);
+  helper->AddMaximizeModeObserver(this);
+  helper->AddInputDeviceEventObserver(this);
   OnWindowFocused(helper->GetFocusedWindow(), nullptr);
 }
 
 Keyboard::~Keyboard() {
   delegate_->OnKeyboardDestroying(this);
+  if (device_configuration_delegate_)
+    device_configuration_delegate_->OnKeyboardDestroying(this);
   if (focus_)
     focus_->RemoveSurfaceObserver(this);
   auto* helper = WMHelper::GetInstance();
   helper->RemoveFocusObserver(this);
   helper->RemovePostTargetHandler(this);
+  helper->RemoveMaximizeModeObserver(this);
+  helper->RemoveInputDeviceEventObserver(this);
+}
+
+void Keyboard::SetDeviceConfigurationDelegate(
+    KeyboardDeviceConfigurationDelegate* delegate) {
+  device_configuration_delegate_ = delegate;
+  OnKeyboardDeviceConfigurationChanged();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -170,6 +201,27 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// ui::InputDeviceEventObserver overrides:
+
+void Keyboard::OnKeyboardDeviceConfigurationChanged() {
+  if (device_configuration_delegate_) {
+    device_configuration_delegate_->OnKeyboardTypeChanged(
+        IsPhysicalKeyboardEnabled());
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WMHelper::MaximizeModeObserver overrides:
+
+void Keyboard::OnMaximizeModeStarted() {
+  OnKeyboardDeviceConfigurationChanged();
+}
+
+void Keyboard::OnMaximizeModeEnded() {
+  OnKeyboardDeviceConfigurationChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // Keyboard, private:
 
 Surface* Keyboard::GetEffectiveFocus(aura::Window* window) const {
diff --git a/components/exo/keyboard.h b/components/exo/keyboard.h
index 9a20afd7..7e86dce 100644
--- a/components/exo/keyboard.h
+++ b/components/exo/keyboard.h
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "components/exo/surface_observer.h"
 #include "components/exo/wm_helper.h"
-#include "ui/aura/client/focus_change_observer.h"
 #include "ui/events/event_handler.h"
 
 namespace ui {
@@ -20,17 +19,23 @@
 
 namespace exo {
 class KeyboardDelegate;
+class KeyboardDeviceConfigurationDelegate;
 class Surface;
 
 // This class implements a client keyboard that represents one or more keyboard
 // devices.
 class Keyboard : public ui::EventHandler,
                  public WMHelper::FocusObserver,
+                 public WMHelper::InputDeviceEventObserver,
+                 public WMHelper::MaximizeModeObserver,
                  public SurfaceObserver {
  public:
   explicit Keyboard(KeyboardDelegate* delegate);
   ~Keyboard() override;
 
+  void SetDeviceConfigurationDelegate(
+      KeyboardDeviceConfigurationDelegate* delegate);
+
   // Overridden from ui::EventHandler:
   void OnKeyEvent(ui::KeyEvent* event) override;
 
@@ -41,13 +46,25 @@
   // Overridden from SurfaceObserver:
   void OnSurfaceDestroying(Surface* surface) override;
 
+  // Overridden from ui::InputDeviceEventObserver:
+  void OnKeyboardDeviceConfigurationChanged() override;
+
+  // Overridden from WMHelper::MaximizeModeObserver:
+  void OnMaximizeModeStarted() override;
+  void OnMaximizeModeEnded() override;
+
  private:
   // Returns the effective focus for |window|.
   Surface* GetEffectiveFocus(aura::Window* window) const;
 
-  // The delegate instance that all events are dispatched to.
+  // The delegate instance that all events except for events about device
+  // configuration are dispatched to.
   KeyboardDelegate* const delegate_;
 
+  // The delegate instance that events about device configuration are dispatched
+  // to.
+  KeyboardDeviceConfigurationDelegate* device_configuration_delegate_ = nullptr;
+
   // The current focus surface for the keyboard.
   Surface* focus_ = nullptr;
 
diff --git a/components/exo/keyboard_delegate.h b/components/exo/keyboard_delegate.h
index 1969067e..619f56a8 100644
--- a/components/exo/keyboard_delegate.h
+++ b/components/exo/keyboard_delegate.h
@@ -36,7 +36,7 @@
   // Called when keyboard focus leaves a valid target surface.
   virtual void OnKeyboardLeave(Surface* surface) = 0;
 
-  // Called when pkeyboard key state changed. |pressed| is true when |key|
+  // Called when keyboard key state changed. |pressed| is true when |key|
   // was pressed and false if it was released.
   virtual void OnKeyboardKey(base::TimeTicks time_stamp,
                              ui::DomCode key,
diff --git a/components/exo/keyboard_device_configuration_delegate.h b/components/exo/keyboard_device_configuration_delegate.h
new file mode 100644
index 0000000..263be3d6
--- /dev/null
+++ b/components/exo/keyboard_device_configuration_delegate.h
@@ -0,0 +1,27 @@
+// 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 COMPONENTS_EXO_KEYBOARD_DEVICE_CONFIGURATION_H_
+#define COMPONENTS_EXO_KEYBOARD_DEVICE_CONFIGURATION_H_
+
+namespace exo {
+class Keyboard;
+
+// Used as an extension to the KeyboardDelegate.
+class KeyboardDeviceConfigurationDelegate {
+ public:
+  // Called at the top of the keyboard's destructor, to give observers a chance
+  // to remove themselves.
+  virtual void OnKeyboardDestroying(Keyboard* keyboard) = 0;
+
+  // Called when used keyboard type changed.
+  virtual void OnKeyboardTypeChanged(bool is_physical) = 0;
+
+ protected:
+  virtual ~KeyboardDeviceConfigurationDelegate() {}
+};
+
+}  // namespace exo
+
+#endif  // COMPONENTS_EXO_KEYBOARD_DEVICE_CONFIGURATION_H_
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 2c23fc3..8c5ed61 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -18,7 +18,9 @@
 #include "cc/quads/texture_draw_quad.h"
 #include "cc/resources/single_release_callback.h"
 #include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_factory.h"
 #include "cc/surfaces/surface_id_allocator.h"
+#include "cc/surfaces/surface_manager.h"
 #include "components/exo/buffer.h"
 #include "components/exo/surface_delegate.h"
 #include "components/exo/surface_observer.h"
@@ -139,24 +141,73 @@
   DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter);
 };
 
+void SatisfyCallback(cc::SurfaceManager* manager,
+                     const cc::SurfaceSequence& sequence) {
+  std::vector<uint32_t> sequences;
+  sequences.push_back(sequence.sequence);
+  manager->DidSatisfySequences(sequence.frame_sink_id, &sequences);
+}
+
+void RequireCallback(cc::SurfaceManager* manager,
+                     const cc::SurfaceId& id,
+                     const cc::SurfaceSequence& sequence) {
+  cc::Surface* surface = manager->GetSurfaceForId(id);
+  if (!surface) {
+    LOG(ERROR) << "Attempting to require callback on nonexistent surface";
+    return;
+  }
+  surface->AddDestructionDependency(sequence);
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
+// SurfaceFactoryOwner, public:
+
+SurfaceFactoryOwner::SurfaceFactoryOwner() {}
+
+////////////////////////////////////////////////////////////////////////////////
+// cc::SurfaceFactoryClient overrides:
+
+void SurfaceFactoryOwner::ReturnResources(
+    const cc::ReturnedResourceArray& resources) {
+  scoped_refptr<SurfaceFactoryOwner> holder(this);
+  for (auto& resource : resources) {
+    auto it = release_callbacks_.find(resource.id);
+    DCHECK(it != release_callbacks_.end());
+    it->second.second->Run(resource.sync_token, resource.lost);
+    release_callbacks_.erase(it);
+  }
+}
+
+void SurfaceFactoryOwner::WillDrawSurface(const cc::LocalFrameId& id,
+                                          const gfx::Rect& damage_rect) {
+  if (surface_)
+    surface_->WillDraw();
+}
+
+void SurfaceFactoryOwner::SetBeginFrameSource(
+    cc::BeginFrameSource* begin_frame_source) {
+  if (surface_)
+    surface_->SetBeginFrameSource(begin_frame_source);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SurfaceFactoryOwner, private:
+
+SurfaceFactoryOwner::~SurfaceFactoryOwner() {
+  if (surface_factory_->manager())
+    surface_factory_->manager()->InvalidateFrameSinkId(frame_sink_id_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // Surface, public:
 
 Surface::Surface()
     : window_(new aura::Window(new CustomWindowDelegate(this))),
-      frame_sink_id_(
-          aura::Env::GetInstance()->context_factory()->AllocateFrameSinkId()) {
-  cc::mojom::MojoCompositorFrameSinkClientPtr frame_sink_holder_ptr;
-  cc::mojom::MojoCompositorFrameSinkClientRequest frame_sink_client_request =
-      mojo::GetProxy(&frame_sink_holder_ptr);
-  std::unique_ptr<CompositorFrameSink> frame_sink(new CompositorFrameSink(
-      frame_sink_id_,
-      aura::Env::GetInstance()->context_factory()->GetSurfaceManager(),
-      std::move(frame_sink_holder_ptr)));
-  compositor_frame_sink_holder_ = new CompositorFrameSinkHolder(
-      this, std::move(frame_sink), std::move(frame_sink_client_request));
+      surface_manager_(
+          aura::Env::GetInstance()->context_factory()->GetSurfaceManager()),
+      factory_owner_(new SurfaceFactoryOwner) {
   window_->SetType(ui::wm::WINDOW_TYPE_CONTROL);
   window_->SetName("ExoSurface");
   window_->SetProperty(kSurfaceKey, this);
@@ -164,6 +215,15 @@
   window_->SetEventTargeter(base::WrapUnique(new CustomWindowTargeter));
   window_->set_owned_by_parent(false);
   window_->AddObserver(this);
+  factory_owner_->surface_ = this;
+  factory_owner_->frame_sink_id_ =
+      aura::Env::GetInstance()->context_factory()->AllocateFrameSinkId();
+  factory_owner_->id_allocator_.reset(new cc::SurfaceIdAllocator());
+  surface_manager_->RegisterFrameSinkId(factory_owner_->frame_sink_id_);
+  surface_manager_->RegisterSurfaceFactoryClient(factory_owner_->frame_sink_id_,
+                                                 factory_owner_.get());
+  factory_owner_->surface_factory_.reset(new cc::SurfaceFactory(
+      factory_owner_->frame_sink_id_, surface_manager_, factory_owner_.get()));
   aura::Env::GetInstance()->context_factory()->AddObserver(this);
 }
 
@@ -175,11 +235,22 @@
   window_->RemoveObserver(this);
   window_->layer()->SetShowSolidColorContent();
 
-  frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
-  compositor_frame_sink_holder_->ActivateFrameCallbacks(&frame_callbacks_);
-  compositor_frame_sink_holder_->CancelFrameCallbacks();
+  factory_owner_->surface_ = nullptr;
 
-  compositor_frame_sink_holder_->GetCompositorFrameSink()->EvictFrame();
+  // Call pending frame callbacks with a null frame time to indicate that they
+  // have been cancelled.
+  frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
+  active_frame_callbacks_.splice(active_frame_callbacks_.end(),
+                                 frame_callbacks_);
+  for (const auto& frame_callback : active_frame_callbacks_)
+    frame_callback.Run(base::TimeTicks());
+  if (begin_frame_source_ && needs_begin_frame_)
+    begin_frame_source_->RemoveObserver(this);
+
+  factory_owner_->surface_factory_->EvictSurface();
+
+  surface_manager_->UnregisterSurfaceFactoryClient(
+      factory_owner_->frame_sink_id_);
 }
 
 // static
@@ -188,7 +259,7 @@
 }
 
 cc::SurfaceId Surface::GetSurfaceId() const {
-  return cc::SurfaceId(frame_sink_id_, local_frame_id_);
+  return cc::SurfaceId(factory_owner_->frame_sink_id_, local_frame_id_);
 }
 
 void Surface::Attach(Buffer* buffer) {
@@ -405,7 +476,7 @@
   cc::LocalFrameId old_local_frame_id = local_frame_id_;
   if (needs_commit_to_new_surface_ || !local_frame_id_.is_valid()) {
     needs_commit_to_new_surface_ = false;
-    local_frame_id_ = id_allocator_.GenerateId();
+    local_frame_id_ = factory_owner_->id_allocator_->GenerateId();
   }
 
   UpdateSurface(true);
@@ -418,11 +489,9 @@
     window_->layer()->SetBounds(
         gfx::Rect(window_->layer()->bounds().origin(), content_size_));
     window_->layer()->SetShowSurface(
-        cc::SurfaceId(frame_sink_id_, local_frame_id_),
-        base::Bind(&CompositorFrameSinkHolder::Satisfy,
-                   compositor_frame_sink_holder_),
-        base::Bind(&CompositorFrameSinkHolder::Require,
-                   compositor_frame_sink_holder_),
+        cc::SurfaceId(factory_owner_->frame_sink_id_, local_frame_id_),
+        base::Bind(&SatisfyCallback, base::Unretained(surface_manager_)),
+        base::Bind(&RequireCallback, base::Unretained(surface_manager_)),
         content_size_, contents_surface_to_layer_scale, content_size_);
     window_->layer()->SetFillsBoundsOpaquely(
         state_.blend_mode == SkBlendMode::kSrc ||
@@ -434,8 +503,7 @@
   pending_damage_.setEmpty();
 
   DCHECK(!current_resource_.id ||
-         compositor_frame_sink_holder_->HasReleaseCallbackForResource(
-             current_resource_.id));
+         factory_owner_->release_callbacks_.count(current_resource_.id));
 
   // Move pending frame callbacks to the end of frame_callbacks_.
   frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
@@ -540,7 +608,18 @@
 }
 
 void Surface::WillDraw() {
-  compositor_frame_sink_holder_->ActivateFrameCallbacks(&frame_callbacks_);
+  active_frame_callbacks_.splice(active_frame_callbacks_.end(),
+                                 frame_callbacks_);
+  UpdateNeedsBeginFrame();
+}
+
+void Surface::SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) {
+  if (begin_frame_source_ && needs_begin_frame_) {
+    begin_frame_source_->RemoveObserver(this);
+    needs_begin_frame_ = false;
+  }
+  begin_frame_source_ = begin_frame_source;
+  UpdateNeedsBeginFrame();
 }
 
 void Surface::CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces() {
@@ -557,12 +636,26 @@
 }
 
 void Surface::OnWindowAddedToRootWindow(aura::Window* window) {
-  window->layer()->GetCompositor()->AddFrameSink(frame_sink_id_);
+  window->layer()->GetCompositor()->AddFrameSink(
+      factory_owner_->frame_sink_id_);
 }
 
 void Surface::OnWindowRemovingFromRootWindow(aura::Window* window,
                                              aura::Window* new_root) {
-  window->layer()->GetCompositor()->RemoveFrameSink(frame_sink_id_);
+  window->layer()->GetCompositor()->RemoveFrameSink(
+      factory_owner_->frame_sink_id_);
+}
+
+void Surface::OnBeginFrame(const cc::BeginFrameArgs& args) {
+  while (!active_frame_callbacks_.empty()) {
+    active_frame_callbacks_.front().Run(args.frame_time);
+    active_frame_callbacks_.pop_front();
+  }
+  last_begin_frame_args_ = args;
+}
+
+const cc::BeginFrameArgs& Surface::LastUsedBeginFrameArgs() const {
+  return last_begin_frame_args_;
 }
 
 Surface::State::State() : input_region(SkIRect::MakeLargest()) {}
@@ -650,8 +743,8 @@
                                                  texture_mailbox.target());
     resource.is_overlay_candidate = texture_mailbox.is_overlay_candidate();
 
-    compositor_frame_sink_holder_->AddResourceReleaseCallback(
-        resource.id, std::move(texture_mailbox_release_callback));
+    factory_owner_->release_callbacks_[resource.id] = std::make_pair(
+        factory_owner_, std::move(texture_mailbox_release_callback));
     current_resource_ = resource;
   } else {
     current_resource_.id = 0;
@@ -741,8 +834,24 @@
   }
 
   frame.render_pass_list.push_back(std::move(render_pass));
-  compositor_frame_sink_holder_->GetCompositorFrameSink()
-      ->SubmitCompositorFrame(local_frame_id_, std::move(frame));
+
+  factory_owner_->surface_factory_->SubmitCompositorFrame(
+      local_frame_id_, std::move(frame), cc::SurfaceFactory::DrawCallback());
+}
+
+void Surface::UpdateNeedsBeginFrame() {
+  if (!begin_frame_source_)
+    return;
+
+  bool needs_begin_frame = !active_frame_callbacks_.empty();
+  if (needs_begin_frame == needs_begin_frame_)
+    return;
+
+  needs_begin_frame_ = needs_begin_frame;
+  if (needs_begin_frame)
+    begin_frame_source_->AddObserver(this);
+  else
+    begin_frame_source_->RemoveObserver(this);
 }
 
 int64_t Surface::SetPropertyInternal(const void* key,
diff --git a/components/exo/surface.h b/components/exo/surface.h
index c0dd787..032cd57f 100644
--- a/components/exo/surface.h
+++ b/components/exo/surface.h
@@ -17,9 +17,7 @@
 #include "base/observer_list.h"
 #include "cc/resources/transferable_resource.h"
 #include "cc/scheduler/begin_frame_source.h"
-#include "cc/surfaces/surface_id_allocator.h"
-#include "components/exo/compositor_frame_sink.h"
-#include "components/exo/compositor_frame_sink_holder.h"
+#include "cc/surfaces/surface_factory_client.h"
 #include "third_party/skia/include/core/SkBlendMode.h"
 #include "third_party/skia/include/core/SkRegion.h"
 #include "ui/aura/window.h"
@@ -33,6 +31,7 @@
 }
 
 namespace cc {
+class SurfaceFactory;
 class SurfaceIdAllocator;
 }
 
@@ -58,9 +57,42 @@
 // change in the future when better hardware cursor support is added.
 using CursorProvider = Pointer;
 
+// This class owns the SurfaceFactory and keeps track of references to the
+// contents of Buffers. It's keeped alive by references from
+// release_callbacks_. It's destroyed when its owning Surface is destroyed and
+// the last outstanding release callback is called.
+class SurfaceFactoryOwner : public base::RefCounted<SurfaceFactoryOwner>,
+                            public cc::SurfaceFactoryClient {
+ public:
+  SurfaceFactoryOwner();
+
+  // Overridden from cc::SurfaceFactoryClient:
+  void ReturnResources(const cc::ReturnedResourceArray& resources) override;
+  void WillDrawSurface(const cc::LocalFrameId& id,
+                       const gfx::Rect& damage_rect) override;
+  void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) override;
+
+ private:
+  friend class base::RefCounted<SurfaceFactoryOwner>;
+  friend class Surface;
+
+  ~SurfaceFactoryOwner() override;
+
+  std::map<int,
+           std::pair<scoped_refptr<SurfaceFactoryOwner>,
+                     std::unique_ptr<cc::SingleReleaseCallback>>>
+      release_callbacks_;
+  cc::FrameSinkId frame_sink_id_;
+  std::unique_ptr<cc::SurfaceIdAllocator> id_allocator_;
+  std::unique_ptr<cc::SurfaceFactory> surface_factory_;
+  Surface* surface_ = nullptr;
+};
+
 // This class represents a rectangular area that is displayed on the screen.
 // It has a location, size and pixel contents.
-class Surface : public ui::ContextFactoryObserver, public aura::WindowObserver {
+class Surface : public ui::ContextFactoryObserver,
+                public aura::WindowObserver,
+                public cc::BeginFrameObserver {
  public:
   using PropertyDeallocator = void (*)(int64_t value);
 
@@ -72,12 +104,9 @@
 
   aura::Window* window() { return window_.get(); }
 
+  const cc::LocalFrameId& local_frame_id() const { return local_frame_id_; }
   cc::SurfaceId GetSurfaceId() const;
 
-  CompositorFrameSinkHolder* compositor_frame_sink_holder() {
-    return compositor_frame_sink_holder_.get();
-  }
-
   // Set a buffer as the content of this surface. A buffer can only be attached
   // to one surface at a time.
   void Attach(Buffer* buffer);
@@ -200,6 +229,11 @@
   void OnWindowRemovingFromRootWindow(aura::Window* window,
                                       aura::Window* new_root) override;
 
+  // Overridden from cc::BeginFrameObserver:
+  void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+  const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
+  void OnBeginFrameSourcePausedChanged(bool paused) override {}
+
   // Sets the |value| of the given surface |property|. Setting to the default
   // value (e.g., NULL) removes the property. The caller is responsible for the
   // lifetime of any object set as a property on the Surface.
@@ -280,6 +314,9 @@
   // current_resource_.
   void UpdateSurface(bool full_damage);
 
+  // Adds/Removes begin frame observer based on state.
+  void UpdateNeedsBeginFrame();
+
   int64_t SetPropertyInternal(const void* key,
                               const char* name,
                               PropertyDeallocator deallocator,
@@ -313,13 +350,13 @@
   // The buffer that will become the content of surface when Commit() is called.
   BufferAttachment pending_buffer_;
 
-  const cc::FrameSinkId frame_sink_id_;
+  cc::SurfaceManager* surface_manager_;
+
+  scoped_refptr<SurfaceFactoryOwner> factory_owner_;
+
+  // The Surface Id currently attached to the window.
   cc::LocalFrameId local_frame_id_;
 
-  scoped_refptr<CompositorFrameSinkHolder> compositor_frame_sink_holder_;
-
-  cc::SurfaceIdAllocator id_allocator_;
-
   // The next resource id the buffer will be attached to.
   int next_resource_id_ = 1;
 
@@ -333,6 +370,7 @@
   // be drawn. They fire at the first begin frame notification after this.
   std::list<FrameCallback> pending_frame_callbacks_;
   std::list<FrameCallback> frame_callbacks_;
+  std::list<FrameCallback> active_frame_callbacks_;
 
   // This is the state that has yet to be committed.
   State pending_state_;
@@ -369,6 +407,11 @@
   // maintains.
   SurfaceDelegate* delegate_ = nullptr;
 
+  // The begin frame source being observed.
+  cc::BeginFrameSource* begin_frame_source_ = nullptr;
+  cc::BeginFrameArgs last_begin_frame_args_;
+  bool needs_begin_frame_ = false;
+
   struct Value {
     const char* name;
     int64_t value;
diff --git a/components/exo/surface_unittest.cc b/components/exo/surface_unittest.cc
index 2e82a57..570e4ac 100644
--- a/components/exo/surface_unittest.cc
+++ b/components/exo/surface_unittest.cc
@@ -50,11 +50,6 @@
   // attached buffer.
   surface->Attach(nullptr);
   surface->Commit();
-  // CompositorFrameSinkHolder::ReclaimResources() gets called via
-  // MojoCompositorFrameSinkClient interface. We need to wait here for the mojo
-  // call to finish so that the release callback finishes running before
-  // the assertion below.
-  RunAllPendingInMessageLoop();
   ASSERT_EQ(1, release_buffer_call_count);
 }
 
@@ -210,7 +205,6 @@
   surface->Attach(buffer.get());
   surface->SetBlendMode(SkBlendMode::kSrc);
   surface->Commit();
-  RunAllPendingInMessageLoop();
 
   const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
   ASSERT_EQ(1u, frame.render_pass_list.size());
@@ -228,7 +222,6 @@
 
   surface->Attach(buffer.get());
   surface->Commit();
-  RunAllPendingInMessageLoop();
 
   const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
   ASSERT_EQ(1u, frame.render_pass_list.size());
diff --git a/components/exo/test/run_all_unittests.cc b/components/exo/test/run_all_unittests.cc
index eedc4d93..e69d387 100644
--- a/components/exo/test/run_all_unittests.cc
+++ b/components/exo/test/run_all_unittests.cc
@@ -6,17 +6,9 @@
 #include "base/bind.h"
 #include "base/test/launcher/unit_test_launcher.h"
 
-#if !defined(OS_IOS)
-#include "mojo/edk/embedder/embedder.h"
-#endif
-
 int main(int argc, char** argv) {
   ash::test::AuraShellTestSuite test_suite(argc, argv);
 
-#if !defined(OS_IOS)
-  mojo::edk::Init();
-#endif
-
   return base::LaunchUnitTestsSerially(
       argc, argv, base::Bind(&ash::test::AuraShellTestSuite::Run,
                              base::Unretained(&test_suite)));
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index 237e3f5..cdd54c2 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -39,6 +39,7 @@
     "//third_party/wayland:wayland_server",
     "//third_party/wayland-protocols:alpha_compositing_protocol",
     "//third_party/wayland-protocols:gaming_input_protocol",
+    "//third_party/wayland-protocols:keyboard_configuration_protocol",
     "//third_party/wayland-protocols:remote_shell_protocol",
     "//third_party/wayland-protocols:secure_output_protocol",
     "//third_party/wayland-protocols:stylus_protocol",
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 46af2b34..f3fc2af 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -13,13 +13,14 @@
 #include <wayland-server-protocol-core.h>
 
 // Note: core wayland headers need to be included before protocol headers.
-#include <alpha-compositing-unstable-v1-server-protocol.h>  // NOLINT
-#include <gaming-input-unstable-v1-server-protocol.h>       // NOLINT
-#include <remote-shell-unstable-v1-server-protocol.h>       // NOLINT
-#include <secure-output-unstable-v1-server-protocol.h>      // NOLINT
-#include <stylus-unstable-v1-server-protocol.h>             // NOLINT
-#include <vsync-feedback-unstable-v1-server-protocol.h>     // NOLINT
-#include <xdg-shell-unstable-v5-server-protocol.h>          // NOLINT
+#include <alpha-compositing-unstable-v1-server-protocol.h>       // NOLINT
+#include <keyboard-configuration-unstable-v1-server-protocol.h>  // NOLINT
+#include <gaming-input-unstable-v1-server-protocol.h>            // NOLINT
+#include <remote-shell-unstable-v1-server-protocol.h>            // NOLINT
+#include <secure-output-unstable-v1-server-protocol.h>           // NOLINT
+#include <stylus-unstable-v1-server-protocol.h>                  // NOLINT
+#include <vsync-feedback-unstable-v1-server-protocol.h>          // NOLINT
+#include <xdg-shell-unstable-v5-server-protocol.h>               // NOLINT
 
 #include <algorithm>
 #include <cstdlib>
@@ -47,6 +48,7 @@
 #include "components/exo/gamepad_delegate.h"
 #include "components/exo/keyboard.h"
 #include "components/exo/keyboard_delegate.h"
+#include "components/exo/keyboard_device_configuration_delegate.h"
 #include "components/exo/notification_surface.h"
 #include "components/exo/notification_surface_manager.h"
 #include "components/exo/pointer.h"
@@ -2984,6 +2986,7 @@
                                wl_resource* resource,
                                uint32_t id,
                                wl_resource* pointer_resource) {
+  // TODO(yhanada): Produce an erorr if a delegate already exists.
   Pointer* pointer = GetUserDataAs<Pointer>(pointer_resource);
 
   wl_resource* stylus_resource =
@@ -3004,6 +3007,83 @@
                                  nullptr);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// keyboard_device_configuration interface:
+
+class WaylandKeyboardDeviceConfigurationDelegate
+    : public KeyboardDeviceConfigurationDelegate {
+ public:
+  WaylandKeyboardDeviceConfigurationDelegate(wl_resource* resource,
+                                             Keyboard* keyboard)
+      : resource_(resource), keyboard_(keyboard) {
+    keyboard_->SetDeviceConfigurationDelegate(this);
+  }
+  ~WaylandKeyboardDeviceConfigurationDelegate() override {
+    if (keyboard_)
+      keyboard_->SetDeviceConfigurationDelegate(nullptr);
+  }
+
+  void OnKeyboardDestroying(Keyboard* keyboard) override {
+    keyboard_ = nullptr;
+  }
+  void OnKeyboardTypeChanged(bool is_physical) override {
+    zcr_keyboard_device_configuration_v1_send_type_change(
+        resource_,
+        is_physical
+            ? ZCR_KEYBOARD_DEVICE_CONFIGURATION_V1_KEYBOARD_TYPE_PHYSICAL
+            : ZCR_KEYBOARD_DEVICE_CONFIGURATION_V1_KEYBOARD_TYPE_VIRTUAL);
+  }
+
+ private:
+  wl_resource* resource_;
+  Keyboard* keyboard_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandKeyboardDeviceConfigurationDelegate);
+};
+
+void keyboard_device_configuration_destroy(wl_client* client,
+                                           wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+const struct zcr_keyboard_device_configuration_v1_interface
+    keyboard_device_configuration_implementation = {
+        keyboard_device_configuration_destroy};
+
+////////////////////////////////////////////////////////////////////////////////
+// keyboard_configuration interface:
+
+void keyboard_configuration_get_keyboard_device_configuration(
+    wl_client* client,
+    wl_resource* resource,
+    uint32_t id,
+    wl_resource* keyboard_resource) {
+  // TODO(yhanada): Produce an error if a delegate already exists.
+  wl_resource* keyboard_device_configuration_resource = wl_resource_create(
+      client, &zcr_keyboard_device_configuration_v1_interface, 1, id);
+
+  SetImplementation(
+      keyboard_device_configuration_resource,
+      &keyboard_device_configuration_implementation,
+      base::MakeUnique<WaylandKeyboardDeviceConfigurationDelegate>(
+          keyboard_device_configuration_resource,
+          GetUserDataAs<Keyboard>(keyboard_resource)));
+}
+
+const struct zcr_keyboard_configuration_v1_interface
+    keyboard_configuration_implementation = {
+        keyboard_configuration_get_keyboard_device_configuration};
+
+void bind_keyboard_configuration(wl_client* client,
+                                 void* data,
+                                 uint32_t version,
+                                 uint32_t id) {
+  wl_resource* resource = wl_resource_create(
+      client, &zcr_keyboard_configuration_v1_interface, version, id);
+  wl_resource_set_implementation(
+      resource, &keyboard_configuration_implementation, data, nullptr);
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -3046,6 +3126,8 @@
                    display_, bind_gaming_input);
   wl_global_create(wl_display_.get(), &zcr_stylus_v1_interface, 1, display_,
                    bind_stylus);
+  wl_global_create(wl_display_.get(), &zcr_keyboard_configuration_v1_interface,
+                   1, display_, bind_keyboard_configuration);
 }
 
 Server::~Server() {}
diff --git a/components/exo/wm_helper.cc b/components/exo/wm_helper.cc
index 6be7aff..ce51cbaf 100644
--- a/components/exo/wm_helper.cc
+++ b/components/exo/wm_helper.cc
@@ -70,6 +70,15 @@
   accessibility_observers_.RemoveObserver(observer);
 }
 
+void WMHelper::AddInputDeviceEventObserver(InputDeviceEventObserver* observer) {
+  input_device_event_observers_.AddObserver(observer);
+}
+
+void WMHelper::RemoveInputDeviceEventObserver(
+    InputDeviceEventObserver* observer) {
+  input_device_event_observers_.RemoveObserver(observer);
+}
+
 void WMHelper::NotifyWindowActivated(aura::Window* gained_active,
                                      aura::Window* lost_active) {
   for (ActivationObserver& observer : activation_observers_)
@@ -107,4 +116,9 @@
     observer.OnAccessibilityModeChanged();
 }
 
+void WMHelper::NotifyKeyboardDeviceConfigurationChanged() {
+  for (InputDeviceEventObserver& observer : input_device_event_observers_)
+    observer.OnKeyboardDeviceConfigurationChanged();
+}
+
 }  // namespace exo
diff --git a/components/exo/wm_helper.h b/components/exo/wm_helper.h
index 306c1f11c..d814e5bc 100644
--- a/components/exo/wm_helper.h
+++ b/components/exo/wm_helper.h
@@ -70,6 +70,14 @@
     virtual ~AccessibilityObserver() {}
   };
 
+  class InputDeviceEventObserver {
+   public:
+    virtual void OnKeyboardDeviceConfigurationChanged() = 0;
+
+   protected:
+    virtual ~InputDeviceEventObserver() {}
+  };
+
   virtual ~WMHelper();
 
   static void SetInstance(WMHelper* helper);
@@ -85,6 +93,8 @@
   void RemoveMaximizeModeObserver(MaximizeModeObserver* observer);
   void AddAccessibilityObserver(AccessibilityObserver* observer);
   void RemoveAccessibilityObserver(AccessibilityObserver* observer);
+  void AddInputDeviceEventObserver(InputDeviceEventObserver* observer);
+  void RemoveInputDeviceEventObserver(InputDeviceEventObserver* observer);
 
   virtual const display::ManagedDisplayInfo GetDisplayInfo(
       int64_t display_id) const = 0;
@@ -113,6 +123,7 @@
   void NotifyMaximizeModeStarted();
   void NotifyMaximizeModeEnded();
   void NotifyAccessibilityModeChanged();
+  void NotifyKeyboardDeviceConfigurationChanged();
 
  private:
   base::ObserverList<ActivationObserver> activation_observers_;
@@ -120,6 +131,7 @@
   base::ObserverList<CursorObserver> cursor_observers_;
   base::ObserverList<MaximizeModeObserver> maximize_mode_observers_;
   base::ObserverList<AccessibilityObserver> accessibility_observers_;
+  base::ObserverList<InputDeviceEventObserver> input_device_event_observers_;
 
   DISALLOW_COPY_AND_ASSIGN(WMHelper);
 };
diff --git a/components/exo/wm_helper_ash.cc b/components/exo/wm_helper_ash.cc
index f70155c7..9026e20 100644
--- a/components/exo/wm_helper_ash.cc
+++ b/components/exo/wm_helper_ash.cc
@@ -11,6 +11,7 @@
 #include "base/memory/singleton.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/display/manager/display_manager.h"
+#include "ui/events/devices/device_data_manager.h"
 #include "ui/wm/public/activation_client.h"
 
 namespace exo {
@@ -24,6 +25,7 @@
   aura::client::FocusClient* focus_client =
       aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
   focus_client->AddObserver(this);
+  ui::DeviceDataManager::GetInstance()->AddObserver(this);
 }
 
 WMHelperAsh::~WMHelperAsh() {
@@ -34,6 +36,7 @@
   focus_client->RemoveObserver(this);
   ash::Shell::GetInstance()->activation_client()->RemoveObserver(this);
   ash::WmShell::Get()->RemoveShellObserver(this);
+  ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -133,4 +136,8 @@
   NotifyMaximizeModeEnded();
 }
 
+void WMHelperAsh::OnKeyboardDeviceConfigurationChanged() {
+  NotifyKeyboardDeviceConfigurationChanged();
+}
+
 }  // namespace exo
diff --git a/components/exo/wm_helper_ash.h b/components/exo/wm_helper_ash.h
index 6479789..ce44f94 100644
--- a/components/exo/wm_helper_ash.h
+++ b/components/exo/wm_helper_ash.h
@@ -11,6 +11,7 @@
 #include "components/exo/wm_helper.h"
 #include "ui/aura/client/cursor_client_observer.h"
 #include "ui/aura/client/focus_change_observer.h"
+#include "ui/events/devices/input_device_event_observer.h"
 #include "ui/wm/public/activation_change_observer.h"
 
 namespace exo {
@@ -21,7 +22,8 @@
                     public aura::client::FocusChangeObserver,
                     public aura::client::CursorClientObserver,
                     public ash::AccessibilityObserver,
-                    public ash::ShellObserver {
+                    public ash::ShellObserver,
+                    public ui::InputDeviceEventObserver {
  public:
   WMHelperAsh();
   ~WMHelperAsh() override;
@@ -64,6 +66,9 @@
   void OnMaximizeModeStarted() override;
   void OnMaximizeModeEnded() override;
 
+  // Overriden from ui::InputDeviceEventObserver:
+  void OnKeyboardDeviceConfigurationChanged() override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(WMHelperAsh);
 };
diff --git a/components/exo/wm_helper_mus.cc b/components/exo/wm_helper_mus.cc
index 9a67809e..4a2d372 100644
--- a/components/exo/wm_helper_mus.cc
+++ b/components/exo/wm_helper_mus.cc
@@ -148,4 +148,8 @@
   }
 }
 
+void WMHelperMus::OnKeyboardDeviceConfigurationChanged() {
+  NotifyKeyboardDeviceConfigurationChanged();
+}
+
 }  // namespace exo
diff --git a/components/exo/wm_helper_mus.h b/components/exo/wm_helper_mus.h
index 8ae5d3a..13751b2 100644
--- a/components/exo/wm_helper_mus.h
+++ b/components/exo/wm_helper_mus.h
@@ -9,11 +9,13 @@
 #include "components/exo/wm_helper.h"
 #include "services/ui/public/cpp/window_tree_client_observer.h"
 #include "ui/aura/client/focus_change_observer.h"
+#include "ui/events/devices/input_device_event_observer.h"
 
 namespace exo {
 
 // A helper class for accessing WindowManager related features.
 class WMHelperMus : public WMHelper,
+                    public ui::InputDeviceEventObserver,
                     public ui::WindowTreeClientObserver,
                     public aura::client::FocusChangeObserver {
  public:
@@ -44,6 +46,9 @@
   void OnWindowFocused(aura::Window* gained_focus,
                        aura::Window* lost_focus) override;
 
+  // Overriden from ui::InputDeviceEventObserver:
+  void OnKeyboardDeviceConfigurationChanged() override;
+
  private:
   aura::Window* active_window_;
   aura::Window* focused_window_;
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index fc76b351..bf7ceca0 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -9,6 +9,8 @@
 import("//third_party/protobuf/proto_library.gni")
 import("//tools/grit/grit_rule.gni")
 
+assert(!is_ios, "Policy should not be referenced on iOS")
+
 # To test policy generation for platforms different than your OS, override and
 # enable these flags (but don't check that in!).
 gen_policy_templates_common = true
@@ -44,351 +46,348 @@
   defines = [ "POLICY_COMPONENT_IMPLEMENTATION" ]
 }
 
-if (enable_configuration_policy) {
-  # This protobuf is equivalent to chrome_settings.proto but shares messages
-  # for policies of the same type, so that less classes have to be generated
-  # and compiled.
-  cloud_policy_proto_path = "$target_gen_dir/proto/cloud_policy.proto"
+# This protobuf is equivalent to chrome_settings.proto but shares messages
+# for policies of the same type, so that less classes have to be generated
+# and compiled.
+cloud_policy_proto_path = "$target_gen_dir/proto/cloud_policy.proto"
 
-  # This is the "full" protobuf, which defines one protobuf message per
-  # policy. It is also the format currently used by the server.
-  chrome_settings_proto_path = "$target_gen_dir/proto/chrome_settings.proto"
+# This is the "full" protobuf, which defines one protobuf message per
+# policy. It is also the format currently used by the server.
+chrome_settings_proto_path = "$target_gen_dir/proto/chrome_settings.proto"
 
-  constants_header_path = "$target_gen_dir/policy_constants.h"
-  constants_source_path = "$target_gen_dir/policy_constants.cc"
-  protobuf_decoder_path = "$target_gen_dir/cloud_policy_generated.cc"
-  app_restrictions_path = "$target_gen_dir/app_restrictions.xml"
-  risk_tag_header_path = "$target_gen_dir/risk_tag.h"
+constants_header_path = "$target_gen_dir/policy_constants.h"
+constants_source_path = "$target_gen_dir/policy_constants.cc"
+protobuf_decoder_path = "$target_gen_dir/cloud_policy_generated.cc"
+app_restrictions_path = "$target_gen_dir/app_restrictions.xml"
+risk_tag_header_path = "$target_gen_dir/risk_tag.h"
 
-  action("cloud_policy_code_generate") {
-    script = "tools/generate_policy_source.py"
-    chrome_version_abspath = "//chrome/VERSION"
-    chrome_version_path = rebase_path(chrome_version_abspath, root_build_dir)
+action("cloud_policy_code_generate") {
+  script = "tools/generate_policy_source.py"
+  chrome_version_abspath = "//chrome/VERSION"
+  chrome_version_path = rebase_path(chrome_version_abspath, root_build_dir)
 
-    if (is_chromeos) {
-      chromeos_flag = "1"
-    } else {
-      chromeos_flag = "0"
-    }
+  if (is_chromeos) {
+    chromeos_flag = "1"
+  } else {
+    chromeos_flag = "0"
+  }
 
+  inputs = [
+    chrome_version_abspath,
+    "resources/policy_templates.json",
+  ]
+  outputs = [
+    constants_header_path,
+    constants_source_path,
+    protobuf_decoder_path,
+    chrome_settings_proto_path,
+    cloud_policy_proto_path,
+    app_restrictions_path,
+    risk_tag_header_path,
+  ]
+
+  if (target_os != "android") {
+    outputs -= [ app_restrictions_path ]
+  }
+
+  args = [
+    "--policy-constants-header=" +
+        rebase_path(constants_header_path, root_build_dir),
+    "--policy-constants-source=" +
+        rebase_path(constants_source_path, root_build_dir),
+    "--chrome-settings-protobuf=" +
+        rebase_path(chrome_settings_proto_path, root_build_dir),
+    "--cloud-policy-protobuf=" +
+        rebase_path(cloud_policy_proto_path, root_build_dir),
+    "--cloud-policy-decoder=" +
+        rebase_path(protobuf_decoder_path, root_build_dir),
+    "--app-restrictions-definition=" +
+        rebase_path(app_restrictions_path, root_build_dir),
+    "--risk-tag-header=" + rebase_path(risk_tag_header_path, root_build_dir),
+    chrome_version_path,
+    target_os,
+    chromeos_flag,
+    rebase_path("resources/policy_templates.json", root_build_dir),
+  ]
+}
+
+policy_templates_grd_file = "resources/policy_templates.grd"
+
+grit("grit_policy_templates") {
+  source = policy_templates_grd_file
+  use_qualified_include = true
+  output_dir = "$root_gen_dir/chrome"
+  outputs = []
+  defines = []
+
+  if (gen_policy_templates_common) {
+    outputs += policy_templates_doc_outputs
+    defines += [ "gen_policy_templates_common" ]
+  }
+  if (gen_policy_templates_android) {
+    outputs += policy_templates_android_outputs
+    defines += [ "gen_policy_templates_android" ]
+  }
+  if (gen_policy_templates_linux) {
+    outputs += policy_templates_linux_outputs
+    defines += [ "gen_policy_templates_linux" ]
+  }
+  if (gen_policy_templates_mac) {
+    outputs += policy_templates_mac_outputs
+    defines += [
+      "mac_bundle_id=$chrome_mac_bundle_id",
+      "gen_policy_templates_mac",
+    ]
+  }
+  if (gen_policy_templates_win) {
+    outputs += policy_templates_windows_outputs
+    defines += [ "gen_policy_templates_win" ]
+  }
+}
+
+if (gen_policy_templates_win && is_chrome_branded) {
+  # Creates google.admx and google.adml files that define a common 'Google'
+  # category used for Chrome, Chrome OS and possibly external tools, see
+  # crbug.com/665400.
+  action("create_google_admx") {
+    script = "tools/create_google_admx.py"
     inputs = [
-      chrome_version_abspath,
-      "resources/policy_templates.json",
-    ]
-    outputs = [
-      constants_header_path,
-      constants_source_path,
-      protobuf_decoder_path,
-      chrome_settings_proto_path,
-      cloud_policy_proto_path,
-      app_restrictions_path,
-      risk_tag_header_path,
-    ]
-
-    if (target_os != "android") {
-      outputs -= [ app_restrictions_path ]
-    }
-
-    args = [
-      "--policy-constants-header=" +
-          rebase_path(constants_header_path, root_build_dir),
-      "--policy-constants-source=" +
-          rebase_path(constants_source_path, root_build_dir),
-      "--chrome-settings-protobuf=" +
-          rebase_path(chrome_settings_proto_path, root_build_dir),
-      "--cloud-policy-protobuf=" +
-          rebase_path(cloud_policy_proto_path, root_build_dir),
-      "--cloud-policy-decoder=" +
-          rebase_path(protobuf_decoder_path, root_build_dir),
-      "--app-restrictions-definition=" +
-          rebase_path(app_restrictions_path, root_build_dir),
-      "--risk-tag-header=" + rebase_path(risk_tag_header_path, root_build_dir),
-      chrome_version_path,
-      target_os,
-      chromeos_flag,
-      rebase_path("resources/policy_templates.json", root_build_dir),
-    ]
-  }
-
-  policy_templates_grd_file = "resources/policy_templates.grd"
-
-  grit("grit_policy_templates") {
-    source = policy_templates_grd_file
-    use_qualified_include = true
-    output_dir = "$root_gen_dir/chrome"
-    outputs = []
-    defines = []
-
-    if (gen_policy_templates_common) {
-      outputs += policy_templates_doc_outputs
-      defines += [ "gen_policy_templates_common" ]
-    }
-    if (gen_policy_templates_android) {
-      outputs += policy_templates_android_outputs
-      defines += [ "gen_policy_templates_android" ]
-    }
-    if (gen_policy_templates_linux) {
-      outputs += policy_templates_linux_outputs
-      defines += [ "gen_policy_templates_linux" ]
-    }
-    if (gen_policy_templates_mac) {
-      outputs += policy_templates_mac_outputs
-      defines += [
-        "mac_bundle_id=$chrome_mac_bundle_id",
-        "gen_policy_templates_mac",
-      ]
-    }
-    if (gen_policy_templates_win) {
-      outputs += policy_templates_windows_outputs
-      defines += [ "gen_policy_templates_win" ]
-    }
-  }
-
-  if (gen_policy_templates_win && is_chrome_branded) {
-    # Creates google.admx and google.adml files that define a common 'Google'
-    # category used for Chrome, Chrome OS and possibly external tools, see
-    # crbug.com/665400.
-    action("create_google_admx") {
-      script = "tools/create_google_admx.py"
-      inputs = [
-                 policy_templates_grd_file,
-                 grit_info_script,
-               ] + policy_templates_windows_outputs
-      outputs = policy_templates_windows_google_outputs
-      deps = [
-        ":grit_policy_templates",
-      ]
-
-      # Don't pass in outputs directly, it would exceed a limit on Windows!
-      args = [
-               "--basedir",
-               rebase_path(policy_templates_base_dir, root_build_dir),
-               "--grd_strip_path_prefix",
-               "app/policy/",
-               "--grd_input",
-               rebase_path(policy_templates_grd_file, root_build_dir),
-               "--grit_info",
-               rebase_path(grit_info_script, root_build_dir),
-               "-D",
-               "gen_policy_templates_win",
-             ] + grit_defines
-    }
-  }
-
-  group("policy_templates") {
-    public_deps = [
+               policy_templates_grd_file,
+               grit_info_script,
+             ] + policy_templates_windows_outputs
+    outputs = policy_templates_windows_google_outputs
+    deps = [
       ":grit_policy_templates",
     ]
-    if (gen_policy_templates_win && is_chrome_branded) {
-      public_deps += [ ":create_google_admx" ]
-    }
-  }
 
-  # Run the proto compiler over the generated file and make it a component.
-  component("cloud_policy_proto_generated_compile") {
-    public_deps = [
-      ":cloud_policy_proto_generated_compile_proto",
-    ]
+    # Don't pass in outputs directly, it would exceed a limit on Windows!
+    args = [
+             "--basedir",
+             rebase_path(policy_templates_base_dir, root_build_dir),
+             "--grd_strip_path_prefix",
+             "app/policy/",
+             "--grd_input",
+             rebase_path(policy_templates_grd_file, root_build_dir),
+             "--grit_info",
+             rebase_path(grit_info_script, root_build_dir),
+             "-D",
+             "gen_policy_templates_win",
+           ] + grit_defines
   }
-  proto_library("cloud_policy_proto_generated_compile_proto") {
-    visibility = [ ":cloud_policy_proto_generated_compile" ]
+}
+
+group("policy_templates") {
+  public_deps = [
+    ":grit_policy_templates",
+  ]
+  if (gen_policy_templates_win && is_chrome_branded) {
+    public_deps += [ ":create_google_admx" ]
+  }
+}
+
+# Run the proto compiler over the generated file and make it a component.
+component("cloud_policy_proto_generated_compile") {
+  public_deps = [
+    ":cloud_policy_proto_generated_compile_proto",
+  ]
+}
+proto_library("cloud_policy_proto_generated_compile_proto") {
+  visibility = [ ":cloud_policy_proto_generated_compile" ]
+  sources = [
+    cloud_policy_proto_path,
+  ]
+
+  proto_out_dir = "components/policy/proto"
+  cc_generator_options = "dllexport_decl=POLICY_PROTO_EXPORT:"
+  cc_include = "components/policy/proto/policy_proto_export.h"
+  component_build_force_source_set = true
+  defines = [ "POLICY_PROTO_COMPILATION" ]
+
+  deps = [
+    ":cloud_policy_code_generate",
+  ]
+}
+
+# This target builds the "full" protobuf, used for tests only.
+proto_library("chrome_settings_proto") {
+  testonly = true
+  sources = [
+    chrome_settings_proto_path,
+  ]
+  proto_out_dir = "components/policy/proto"
+
+  deps = [
+    ":cloud_policy_code_generate",
+    ":cloud_policy_proto_generated_compile",
+  ]
+}
+
+static_library("generated") {
+  sources = [
+    constants_header_path,
+    constants_source_path,
+    protobuf_decoder_path,
+    risk_tag_header_path,
+  ]
+
+  defines = [ "POLICY_COMPONENT_IMPLEMENTATION" ]
+  public_deps = [
+    ":cloud_policy_code_generate",
+    ":cloud_policy_proto_generated_compile",
+    "//base",
+    "//third_party/protobuf:protobuf_lite",
+  ]
+}
+
+if (gen_policy_templates_android && is_android) {
+  import("//build/config/android/rules.gni")
+
+  _generated_resources_dir = "$root_gen_dir/chrome/app/policy/android"
+
+  copy("app_restrictions_resources_copy") {
     sources = [
-      cloud_policy_proto_path,
+      app_restrictions_path,
     ]
-
-    proto_out_dir = "components/policy/proto"
-    cc_generator_options = "dllexport_decl=POLICY_PROTO_EXPORT:"
-    cc_include = "components/policy/proto/policy_proto_export.h"
-    component_build_force_source_set = true
-    defines = [ "POLICY_PROTO_COMPILATION" ]
-
+    outputs = [
+      "$_generated_resources_dir/xml-v21/app_restrictions.xml",
+    ]
     deps = [
       ":cloud_policy_code_generate",
+      ":policy_templates",
     ]
   }
 
-  # This target builds the "full" protobuf, used for tests only.
-  proto_library("chrome_settings_proto") {
-    testonly = true
-    sources = [
-      chrome_settings_proto_path,
+  android_resources("app_restrictions_resources") {
+    resource_dirs = []
+    generated_resource_dirs = [
+      "$policy_templates_base_dir/android",
+      _generated_resources_dir,
     ]
-    proto_out_dir = "components/policy/proto"
-
+    generated_resource_files =
+        policy_templates_android_outputs +
+        [ "$_generated_resources_dir/xml-v21/app_restrictions.xml" ]
     deps = [
-      ":cloud_policy_code_generate",
-      ":cloud_policy_proto_generated_compile",
+      ":app_restrictions_resources_copy",
+      ":grit_policy_templates",
     ]
   }
-
-  static_library("generated") {
+} else if (gen_policy_templates_mac && is_mac) {
+  action("convert_mcx_plist") {
+    script = "//build/config/mac/xcrun.py"
     sources = [
-      constants_header_path,
-      constants_source_path,
-      protobuf_decoder_path,
-      risk_tag_header_path,
+      "$policy_templates_base_dir/mac/app-Manifest.plist",
+    ]
+    inputs = [
+      script,
+    ]
+    outputs = [
+      "$target_gen_dir/$chrome_mac_bundle_id.manifest",
     ]
 
-    defines = [ "POLICY_COMPONENT_IMPLEMENTATION" ]
-    public_deps = [
-      ":cloud_policy_code_generate",
-      ":cloud_policy_proto_generated_compile",
-      "//base",
-      "//third_party/protobuf:protobuf_lite",
-    ]
-  }
-
-  if (gen_policy_templates_android && is_android) {
-    import("//build/config/android/rules.gni")
-
-    _generated_resources_dir = "$root_gen_dir/chrome/app/policy/android"
-
-    copy("app_restrictions_resources_copy") {
-      sources = [
-        app_restrictions_path,
-      ]
-      outputs = [
-        "$_generated_resources_dir/xml-v21/app_restrictions.xml",
-      ]
-      deps = [
-        ":cloud_policy_code_generate",
-        ":policy_templates",
-      ]
-    }
-
-    android_resources("app_restrictions_resources") {
-      resource_dirs = []
-      generated_resource_dirs = [
-        "$policy_templates_base_dir/android",
-        _generated_resources_dir,
-      ]
-      generated_resource_files =
-          policy_templates_android_outputs +
-          [ "$_generated_resources_dir/xml-v21/app_restrictions.xml" ]
-      deps = [
-        ":app_restrictions_resources_copy",
-        ":grit_policy_templates",
-      ]
-    }
-  } else if (gen_policy_templates_mac && is_mac) {
-    action("convert_mcx_plist") {
-      script = "//build/config/mac/xcrun.py"
-      sources = [
-        "$policy_templates_base_dir/mac/app-Manifest.plist",
-      ]
-      inputs = [
-        script,
-      ]
-      outputs = [
-        "$target_gen_dir/$chrome_mac_bundle_id.manifest",
-      ]
-
-      if (use_system_xcode) {
-        args = []
-      } else {
-        args = [
-          "--developer_dir",
-          hermetic_xcode_path,
-        ]
-      }
-      args += [
-                "plutil",
-                "-convert",
-                "xml1",
-              ] + rebase_path(sources, root_out_dir) + [ "-o" ] +
-              rebase_path(outputs, root_out_dir)
-
-      deps = [
-        ":policy_templates",
-      ]
-    }
-
-    bundle_data("manifest_bundle_data") {
-      sources = get_target_outputs(":convert_mcx_plist")
-      outputs = [
-        "{{bundle_resources_dir}}/{{source_file_part}}",
-      ]
-      public_deps = [
-        ":convert_mcx_plist",
-      ]
-    }
-
-    # The reason we are not enumerating all the locales is that
-    # the translations would eat up 3.5MB disk space in the
-    # application bundle.
-    bundle_data("manifest_strings_bundle_data") {
-      sources = [
-        "$policy_templates_base_dir/mac/strings/en.lproj/Localizable.strings",
-      ]
-      outputs = [
-        "{{bundle_resources_dir}}/en.lproj/{{source_file_part}}",
-      ]
-      public_deps = [
-        ":policy_templates",
-      ]
-    }
-
-    create_bundle("chrome_manifest_bundle") {
-      bundle_root_dir = "$root_out_dir/$chrome_mac_bundle_id.manifest/Contents"
-      bundle_resources_dir = "$bundle_root_dir/Resources"
-
-      deps = [
-        ":manifest_bundle_data",
-        ":manifest_strings_bundle_data",
-      ]
-    }
-  }
-
-  if (gen_policy_templates_common && gen_policy_templates_win) {
-    version_file = "VERSION"
-    version_path = "$policy_templates_base_dir/$version_file"
-
-    copy("add_version") {
-      sources = [
-        "//chrome/VERSION",
-      ]
-      outputs = [
-        version_path,
-      ]
-    }
-
-    action("pack_policy_templates") {
-      output_zip_file = "$root_out_dir/policy_templates.zip"
-      script = "tools/make_policy_zip.py"
-      inputs =
-          [
-            version_path,
-            policy_templates_grd_file,
-            grit_info_script,
-          ] + policy_templates_windows_outputs + policy_templates_doc_outputs
-      outputs = [
-        output_zip_file,
-      ]
+    if (use_system_xcode) {
+      args = []
+    } else {
       args = [
-               "--output",
-               rebase_path(output_zip_file, root_build_dir),
-               "--basedir",
-               rebase_path(policy_templates_base_dir, root_build_dir),
-               "--grd_input",
-               rebase_path(policy_templates_grd_file, root_build_dir),
-               "--grd_strip_path_prefix",
-               "app/policy",
-               "--extra_input",
-               version_file,
-               "--grit_info",
-               rebase_path(grit_info_script, root_build_dir),
-               "-D",
-               "gen_policy_templates_common",
-               "-D",
-               "gen_policy_templates_win",
-             ] + grit_defines
-      if (is_chrome_branded) {
-        args += [ "--include_google_admx" ]
-      }
-      deps = [
-        ":add_version",
-        ":policy_templates",
+        "--developer_dir",
+        hermetic_xcode_path,
       ]
     }
+    args += [
+              "plutil",
+              "-convert",
+              "xml1",
+            ] + rebase_path(sources, root_out_dir) + [ "-o" ] +
+            rebase_path(outputs, root_out_dir)
+
+    deps = [
+      ":policy_templates",
+    ]
+  }
+
+  bundle_data("manifest_bundle_data") {
+    sources = get_target_outputs(":convert_mcx_plist")
+    outputs = [
+      "{{bundle_resources_dir}}/{{source_file_part}}",
+    ]
+    public_deps = [
+      ":convert_mcx_plist",
+    ]
+  }
+
+  # The reason we are not enumerating all the locales is that
+  # the translations would eat up 3.5MB disk space in the
+  # application bundle.
+  bundle_data("manifest_strings_bundle_data") {
+    sources = [
+      "$policy_templates_base_dir/mac/strings/en.lproj/Localizable.strings",
+    ]
+    outputs = [
+      "{{bundle_resources_dir}}/en.lproj/{{source_file_part}}",
+    ]
+    public_deps = [
+      ":policy_templates",
+    ]
+  }
+
+  create_bundle("chrome_manifest_bundle") {
+    bundle_root_dir = "$root_out_dir/$chrome_mac_bundle_id.manifest/Contents"
+    bundle_resources_dir = "$bundle_root_dir/Resources"
+
+    deps = [
+      ":manifest_bundle_data",
+      ":manifest_strings_bundle_data",
+    ]
+  }
+}
+
+if (gen_policy_templates_common && gen_policy_templates_win) {
+  version_file = "VERSION"
+  version_path = "$policy_templates_base_dir/$version_file"
+
+  copy("add_version") {
+    sources = [
+      "//chrome/VERSION",
+    ]
+    outputs = [
+      version_path,
+    ]
+  }
+
+  action("pack_policy_templates") {
+    output_zip_file = "$root_out_dir/policy_templates.zip"
+    script = "tools/make_policy_zip.py"
+    inputs = [
+               version_path,
+               policy_templates_grd_file,
+               grit_info_script,
+             ] + policy_templates_windows_outputs + policy_templates_doc_outputs
+    outputs = [
+      output_zip_file,
+    ]
+    args = [
+             "--output",
+             rebase_path(output_zip_file, root_build_dir),
+             "--basedir",
+             rebase_path(policy_templates_base_dir, root_build_dir),
+             "--grd_input",
+             rebase_path(policy_templates_grd_file, root_build_dir),
+             "--grd_strip_path_prefix",
+             "app/policy",
+             "--extra_input",
+             version_file,
+             "--grit_info",
+             rebase_path(grit_info_script, root_build_dir),
+             "-D",
+             "gen_policy_templates_common",
+             "-D",
+             "gen_policy_templates_win",
+           ] + grit_defines
+    if (is_chrome_branded) {
+      args += [ "--include_google_admx" ]
+    }
+    deps = [
+      ":add_version",
+      ":policy_templates",
+    ]
   }
 }
diff --git a/components/policy/core/browser/BUILD.gn b/components/policy/core/browser/BUILD.gn
index 9b98ff8..c3730e0 100644
--- a/components/policy/core/browser/BUILD.gn
+++ b/components/policy/core/browser/BUILD.gn
@@ -4,6 +4,8 @@
 
 import("//build/config/features.gni")
 
+assert(!is_ios, "Policy should not be referenced on iOS")
+
 group("browser") {
   if (is_component_build) {
     public_deps = [
@@ -19,11 +21,30 @@
 source_set("internal") {
   visibility = [ "//components/policy/*" ]
   sources = [
-    # Note that these sources are always included, even for builds that disable
-    # policy. Most source files should go in the conditional sources list
-    # below. url_blacklist_manager.h is used by managed mode.
+    "autofill_policy_handler.cc",
+    "autofill_policy_handler.h",
+    "browser_policy_connector.cc",
+    "browser_policy_connector.h",
+    "browser_policy_connector_base.cc",
+    "browser_policy_connector_base.h",
+    "browser_policy_connector_ios.h",
+    "browser_policy_connector_ios.mm",
+    "cloud/message_util.cc",
+    "cloud/message_util.h",
+    "configuration_policy_handler.cc",
+    "configuration_policy_handler.h",
+    "configuration_policy_handler_list.cc",
+    "configuration_policy_handler_list.h",
+    "configuration_policy_pref_store.cc",
+    "configuration_policy_pref_store.h",
+    "policy_error_map.cc",
+    "policy_error_map.h",
+    "proxy_policy_handler.cc",
+    "proxy_policy_handler.h",
     "url_blacklist_manager.cc",
     "url_blacklist_manager.h",
+    "url_blacklist_policy_handler.cc",
+    "url_blacklist_policy_handler.h",
   ]
 
   configs += [ "//components/policy:component_implementation" ]
@@ -56,91 +77,62 @@
     deps += [ "//components/policy/android:jni_headers" ]
   }
 
-  if (enable_configuration_policy) {
-    sources += [
-      "autofill_policy_handler.cc",
-      "autofill_policy_handler.h",
-      "browser_policy_connector.cc",
-      "browser_policy_connector.h",
-      "browser_policy_connector_base.cc",
-      "browser_policy_connector_base.h",
-      "browser_policy_connector_ios.h",
-      "browser_policy_connector_ios.mm",
-      "cloud/message_util.cc",
-      "cloud/message_util.h",
-      "configuration_policy_handler.cc",
-      "configuration_policy_handler.h",
-      "configuration_policy_handler_list.cc",
-      "configuration_policy_handler_list.h",
-      "configuration_policy_pref_store.cc",
-      "configuration_policy_pref_store.h",
-      "policy_error_map.cc",
-      "policy_error_map.h",
-      "proxy_policy_handler.cc",
-      "proxy_policy_handler.h",
-      "url_blacklist_policy_handler.cc",
-      "url_blacklist_policy_handler.h",
-    ]
-
-    public_deps += [ "//components/policy/core/common:internal" ]
-    deps += [
-      "//components/autofill/core/common",
-      "//components/proxy_config",
-      "//google_apis",
-      "//net",
-      "//third_party/icu",
-    ]
-  }
+  public_deps += [ "//components/policy/core/common:internal" ]
+  deps += [
+    "//components/autofill/core/common",
+    "//components/proxy_config",
+    "//google_apis",
+    "//net",
+    "//third_party/icu",
+  ]
 }
 
-if (enable_configuration_policy) {
-  static_library("test_support") {
-    testonly = true
-    sources = [
-      "configuration_policy_pref_store_test.cc",
-      "configuration_policy_pref_store_test.h",
-    ]
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "configuration_policy_pref_store_test.cc",
+    "configuration_policy_pref_store_test.h",
+  ]
 
-    public_deps = [
-      ":browser",
-      "//base",
+  public_deps = [
+    ":browser",
+    "//base",
 
-      # Explicitly link in the generated policy target into the test support
-      # so it will be linked to dependent targets. Otherwise in component
-      # build, it will be hidden inside the policy component.
-      "//components/policy:generated",
-      "//components/policy/core/common:test_support",
-    ]
-    deps = [
-      "//testing/gtest",
-    ]
-  }
+    # Explicitly link in the generated policy target into the test support
+    # so it will be linked to dependent targets. Otherwise in component
+    # build, it will be hidden inside the policy component.
+    "//components/policy:generated",
+    "//components/policy/core/common:test_support",
+  ]
+  deps = [
+    "//testing/gtest",
+  ]
+}
 
-  source_set("unit_tests") {
-    testonly = true
-    sources = [
-      "android/android_combined_policy_provider_unittest.cc",
-      "android/policy_converter_unittest.cc",
-      "autofill_policy_handler_unittest.cc",
-      "browser_policy_connector_unittest.cc",
-      "configuration_policy_handler_unittest.cc",
-      "configuration_policy_pref_store_unittest.cc",
-      "proxy_policy_handler_unittest.cc",
-      "url_blacklist_manager_unittest.cc",
-      "url_blacklist_policy_handler_unittest.cc",
-    ]
-    deps = [
-      ":test_support",
-      "//base",
-      "//components/autofill/core/common",
-      "//components/policy:generated",
-      "//components/prefs:test_support",
-      "//components/proxy_config",
-      "//components/url_formatter",
-      "//google_apis",
-      "//net",
-      "//testing/gmock",
-      "//testing/gtest",
-    ]
-  }
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "android/android_combined_policy_provider_unittest.cc",
+    "android/policy_converter_unittest.cc",
+    "autofill_policy_handler_unittest.cc",
+    "browser_policy_connector_unittest.cc",
+    "configuration_policy_handler_unittest.cc",
+    "configuration_policy_pref_store_unittest.cc",
+    "proxy_policy_handler_unittest.cc",
+    "url_blacklist_manager_unittest.cc",
+    "url_blacklist_policy_handler_unittest.cc",
+  ]
+  deps = [
+    ":test_support",
+    "//base",
+    "//components/autofill/core/common",
+    "//components/policy:generated",
+    "//components/prefs:test_support",
+    "//components/proxy_config",
+    "//components/url_formatter",
+    "//google_apis",
+    "//net",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
 }
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index f501530..17a586048 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -21,355 +21,329 @@
 
   configs += [ "//components/policy:component_implementation" ]
 
-  if (enable_configuration_policy) {
-    sources = [
-      "../../policy_export.h",
-      "async_policy_loader.cc",
-      "async_policy_loader.h",
-      "async_policy_provider.cc",
-      "async_policy_provider.h",
-      "cloud/cloud_external_data_manager.cc",
-      "cloud/cloud_external_data_manager.h",
-      "cloud/cloud_policy_client.cc",
-      "cloud/cloud_policy_client.h",
-      "cloud/cloud_policy_client_registration_helper.cc",
-      "cloud/cloud_policy_client_registration_helper.h",
-      "cloud/cloud_policy_constants.cc",
-      "cloud/cloud_policy_constants.h",
-      "cloud/cloud_policy_core.cc",
-      "cloud/cloud_policy_core.h",
-      "cloud/cloud_policy_manager.cc",
-      "cloud/cloud_policy_manager.h",
-      "cloud/cloud_policy_refresh_scheduler.cc",
-      "cloud/cloud_policy_refresh_scheduler.h",
-      "cloud/cloud_policy_service.cc",
-      "cloud/cloud_policy_service.h",
-      "cloud/cloud_policy_store.cc",
-      "cloud/cloud_policy_store.h",
-      "cloud/cloud_policy_validator.cc",
-      "cloud/cloud_policy_validator.h",
+  sources = [
+    "../../policy_export.h",
+    "async_policy_loader.cc",
+    "async_policy_loader.h",
+    "async_policy_provider.cc",
+    "async_policy_provider.h",
+    "cloud/cloud_external_data_manager.cc",
+    "cloud/cloud_external_data_manager.h",
+    "cloud/cloud_policy_client.cc",
+    "cloud/cloud_policy_client.h",
+    "cloud/cloud_policy_client_registration_helper.cc",
+    "cloud/cloud_policy_client_registration_helper.h",
+    "cloud/cloud_policy_constants.cc",
+    "cloud/cloud_policy_constants.h",
+    "cloud/cloud_policy_core.cc",
+    "cloud/cloud_policy_core.h",
+    "cloud/cloud_policy_manager.cc",
+    "cloud/cloud_policy_manager.h",
+    "cloud/cloud_policy_refresh_scheduler.cc",
+    "cloud/cloud_policy_refresh_scheduler.h",
+    "cloud/cloud_policy_service.cc",
+    "cloud/cloud_policy_service.h",
+    "cloud/cloud_policy_store.cc",
+    "cloud/cloud_policy_store.h",
+    "cloud/cloud_policy_validator.cc",
+    "cloud/cloud_policy_validator.h",
+    "cloud/component_cloud_policy_service.cc",
+    "cloud/component_cloud_policy_service.h",
+    "cloud/component_cloud_policy_store.cc",
+    "cloud/component_cloud_policy_store.h",
+    "cloud/component_cloud_policy_updater.cc",
+    "cloud/component_cloud_policy_updater.h",
+    "cloud/device_management_service.cc",
+    "cloud/device_management_service.h",
+    "cloud/enterprise_metrics.cc",
+    "cloud/enterprise_metrics.h",
+    "cloud/external_policy_data_fetcher.cc",
+    "cloud/external_policy_data_fetcher.h",
+    "cloud/external_policy_data_updater.cc",
+    "cloud/external_policy_data_updater.h",
+    "cloud/policy_header_io_helper.cc",
+    "cloud/policy_header_io_helper.h",
+    "cloud/policy_header_service.cc",
+    "cloud/policy_header_service.h",
+    "cloud/resource_cache.cc",
+    "cloud/resource_cache.h",
+    "cloud/signing_service.h",
+    "cloud/user_cloud_policy_manager.cc",
+    "cloud/user_cloud_policy_manager.h",
+    "cloud/user_cloud_policy_store.cc",
+    "cloud/user_cloud_policy_store.h",
+    "cloud/user_cloud_policy_store_base.cc",
+    "cloud/user_cloud_policy_store_base.h",
+    "cloud/user_info_fetcher.cc",
+    "cloud/user_info_fetcher.h",
+    "config_dir_policy_loader.cc",
+    "config_dir_policy_loader.h",
+    "configuration_policy_provider.cc",
+    "configuration_policy_provider.h",
+    "external_data_fetcher.cc",
+    "external_data_fetcher.h",
+    "external_data_manager.h",
+    "policy_bundle.cc",
+    "policy_bundle.h",
+    "policy_details.h",
+    "policy_load_status.cc",
+    "policy_load_status.h",
+    "policy_loader_ios.h",
+    "policy_loader_ios.mm",
+    "policy_loader_mac.h",
+    "policy_loader_mac.mm",
+    "policy_loader_win.cc",
+    "policy_loader_win.h",
+    "policy_map.cc",
+    "policy_map.h",
+    "policy_namespace.cc",
+    "policy_namespace.h",
+    "policy_pref_names.cc",
+    "policy_pref_names.h",
+    "policy_service.cc",
+    "policy_service.h",
+    "policy_service_impl.cc",
+    "policy_service_impl.h",
+    "policy_statistics_collector.cc",
+    "policy_statistics_collector.h",
+    "policy_switches.cc",
+    "policy_switches.h",
+    "policy_types.h",
+    "preferences_mac.cc",
+    "preferences_mac.h",
+    "remote_commands/remote_command_job.cc",
+    "remote_commands/remote_command_job.h",
+    "remote_commands/remote_commands_factory.cc",
+    "remote_commands/remote_commands_factory.h",
+    "remote_commands/remote_commands_queue.cc",
+    "remote_commands/remote_commands_queue.h",
+    "remote_commands/remote_commands_service.cc",
+    "remote_commands/remote_commands_service.h",
+    "schema.cc",
+    "schema.h",
+    "schema_internal.h",
+    "schema_map.cc",
+    "schema_map.h",
+    "schema_registry.cc",
+    "schema_registry.h",
+    "schema_registry_tracking_policy_provider.cc",
+    "schema_registry_tracking_policy_provider.h",
+  ]
+
+  configs += [ "//build/config:precompiled_headers" ]
+
+  public_deps = [
+    "//components/policy:generated",
+    "//components/policy/proto",
+  ]
+  deps = [
+    "//base:i18n",
+    "//base/third_party/dynamic_annotations",
+    "//components/data_use_measurement/core",
+    "//components/json_schema",
+    "//components/prefs",
+    "//extensions/features",
+    "//google_apis",
+    "//net",
+    "//third_party/re2",
+    "//url",
+  ]
+
+  if (is_win) {
+    libs = [
+      "shlwapi.lib",
+      "userenv.lib",
+      "ntdsapi.lib",
+    ]
+  }
+  if (is_win || is_chromeos) {
+    sources += [
+      "preg_parser.cc",
+      "preg_parser.h",
+      "registry_dict.cc",
+      "registry_dict.h",
+    ]
+  }
+  if (is_android) {
+    sources += [ "cloud/component_cloud_policy_service_stub.cc" ]
+    sources -= [
       "cloud/component_cloud_policy_service.cc",
-      "cloud/component_cloud_policy_service.h",
       "cloud/component_cloud_policy_store.cc",
       "cloud/component_cloud_policy_store.h",
       "cloud/component_cloud_policy_updater.cc",
       "cloud/component_cloud_policy_updater.h",
-      "cloud/device_management_service.cc",
-      "cloud/device_management_service.h",
-      "cloud/enterprise_metrics.cc",
-      "cloud/enterprise_metrics.h",
       "cloud/external_policy_data_fetcher.cc",
       "cloud/external_policy_data_fetcher.h",
       "cloud/external_policy_data_updater.cc",
       "cloud/external_policy_data_updater.h",
-      "cloud/policy_header_io_helper.cc",
-      "cloud/policy_header_io_helper.h",
-      "cloud/policy_header_service.cc",
-      "cloud/policy_header_service.h",
       "cloud/resource_cache.cc",
       "cloud/resource_cache.h",
-      "cloud/signing_service.h",
+      "config_dir_policy_loader.cc",
+      "config_dir_policy_loader.h",
+      "policy_load_status.cc",
+      "policy_load_status.h",
+    ]
+  }
+  if (is_chromeos) {
+    sources += [
+      "proxy_policy_provider.cc",
+      "proxy_policy_provider.h",
+    ]
+    sources -= [
+      "cloud/cloud_policy_client_registration_helper.cc",
+      "cloud/cloud_policy_client_registration_helper.h",
       "cloud/user_cloud_policy_manager.cc",
       "cloud/user_cloud_policy_manager.h",
       "cloud/user_cloud_policy_store.cc",
       "cloud/user_cloud_policy_store.h",
-      "cloud/user_cloud_policy_store_base.cc",
-      "cloud/user_cloud_policy_store_base.h",
-      "cloud/user_info_fetcher.cc",
-      "cloud/user_info_fetcher.h",
-      "config_dir_policy_loader.cc",
-      "config_dir_policy_loader.h",
-      "configuration_policy_provider.cc",
-      "configuration_policy_provider.h",
-      "external_data_fetcher.cc",
-      "external_data_fetcher.h",
-      "external_data_manager.h",
-      "policy_bundle.cc",
-      "policy_bundle.h",
-      "policy_details.h",
-      "policy_load_status.cc",
-      "policy_load_status.h",
-      "policy_loader_ios.h",
-      "policy_loader_ios.mm",
-      "policy_loader_mac.h",
-      "policy_loader_mac.mm",
-      "policy_loader_win.cc",
-      "policy_loader_win.h",
-      "policy_map.cc",
-      "policy_map.h",
-      "policy_namespace.cc",
-      "policy_namespace.h",
-      "policy_pref_names.cc",
-      "policy_pref_names.h",
-      "policy_service.cc",
-      "policy_service.h",
-      "policy_service_impl.cc",
-      "policy_service_impl.h",
-      "policy_statistics_collector.cc",
-      "policy_statistics_collector.h",
-      "policy_switches.cc",
-      "policy_switches.h",
-      "policy_types.h",
-      "preferences_mac.cc",
-      "preferences_mac.h",
-      "remote_commands/remote_command_job.cc",
-      "remote_commands/remote_command_job.h",
-      "remote_commands/remote_commands_factory.cc",
-      "remote_commands/remote_commands_factory.h",
-      "remote_commands/remote_commands_queue.cc",
-      "remote_commands/remote_commands_queue.h",
-      "remote_commands/remote_commands_service.cc",
-      "remote_commands/remote_commands_service.h",
-      "schema.cc",
-      "schema.h",
-      "schema_internal.h",
-      "schema_map.cc",
-      "schema_map.h",
-      "schema_registry.cc",
-      "schema_registry.h",
-      "schema_registry_tracking_policy_provider.cc",
-      "schema_registry_tracking_policy_provider.h",
     ]
-
-    configs += [ "//build/config:precompiled_headers" ]
-
-    public_deps = [
-      "//components/policy:generated",
-      "//components/policy/proto",
-    ]
-    deps = [
-      "//base:i18n",
-      "//base/third_party/dynamic_annotations",
-      "//components/data_use_measurement/core",
-      "//components/json_schema",
-      "//components/prefs",
-      "//extensions/features",
-      "//google_apis",
-      "//net",
-      "//third_party/re2",
-      "//url",
-    ]
-
-    if (is_win) {
-      libs = [
-        "shlwapi.lib",
-        "userenv.lib",
-        "ntdsapi.lib",
-      ]
-    }
-    if (is_win || is_chromeos) {
-      sources += [
-        "preg_parser.cc",
-        "preg_parser.h",
-        "registry_dict.cc",
-        "registry_dict.h",
-      ]
-    }
-    if (is_android) {
-      sources += [ "cloud/component_cloud_policy_service_stub.cc" ]
-      sources -= [
-        "cloud/component_cloud_policy_service.cc",
-        "cloud/component_cloud_policy_store.cc",
-        "cloud/component_cloud_policy_store.h",
-        "cloud/component_cloud_policy_updater.cc",
-        "cloud/component_cloud_policy_updater.h",
-        "cloud/external_policy_data_fetcher.cc",
-        "cloud/external_policy_data_fetcher.h",
-        "cloud/external_policy_data_updater.cc",
-        "cloud/external_policy_data_updater.h",
-        "cloud/resource_cache.cc",
-        "cloud/resource_cache.h",
-        "config_dir_policy_loader.cc",
-        "config_dir_policy_loader.h",
-        "policy_load_status.cc",
-        "policy_load_status.h",
-      ]
-    }
-    if (is_chromeos) {
-      sources += [
-        "proxy_policy_provider.cc",
-        "proxy_policy_provider.h",
-      ]
-      sources -= [
-        "cloud/cloud_policy_client_registration_helper.cc",
-        "cloud/cloud_policy_client_registration_helper.h",
-        "cloud/user_cloud_policy_manager.cc",
-        "cloud/user_cloud_policy_manager.h",
-        "cloud/user_cloud_policy_store.cc",
-        "cloud/user_cloud_policy_store.h",
-      ]
-    }
-    if (is_mac) {
-      libs = [ "CoreFoundation.framework" ]
-    }
-    if (is_ios || is_mac) {
-      sources += [
-        "mac_util.cc",
-        "mac_util.h",
-      ]
-    }
-  } else {
-    # Some of the policy code is always enabled, so that other parts of Chrome
-    # can always interface with the PolicyService without having to #ifdef on
-    # ENABLE_CONFIGURATION_POLICY.
-    sources = [
-      "external_data_fetcher.cc",
-      "external_data_fetcher.h",
-      "external_data_manager.h",
-      "policy_map.cc",
-      "policy_map.h",
-      "policy_namespace.cc",
-      "policy_namespace.h",
-      "policy_pref_names.cc",
-      "policy_pref_names.h",
-      "policy_service.cc",
-      "policy_service.h",
-      "policy_service_stub.cc",
-      "policy_service_stub.h",
-    ]
-    deps = [
-      "//base",
+  }
+  if (is_mac) {
+    libs = [ "CoreFoundation.framework" ]
+  }
+  if (is_ios || is_mac) {
+    sources += [
+      "mac_util.cc",
+      "mac_util.h",
     ]
   }
 }
 
-if (enable_configuration_policy) {
-  static_library("test_support") {
-    testonly = true
-    sources = [
-      "cloud/mock_cloud_external_data_manager.cc",
-      "cloud/mock_cloud_external_data_manager.h",
-      "cloud/mock_cloud_policy_client.cc",
-      "cloud/mock_cloud_policy_client.h",
-      "cloud/mock_cloud_policy_store.cc",
-      "cloud/mock_cloud_policy_store.h",
-      "cloud/mock_device_management_service.cc",
-      "cloud/mock_device_management_service.h",
-      "cloud/mock_signing_service.cc",
-      "cloud/mock_signing_service.h",
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "cloud/mock_cloud_external_data_manager.cc",
+    "cloud/mock_cloud_external_data_manager.h",
+    "cloud/mock_cloud_policy_client.cc",
+    "cloud/mock_cloud_policy_client.h",
+    "cloud/mock_cloud_policy_store.cc",
+    "cloud/mock_cloud_policy_store.h",
+    "cloud/mock_device_management_service.cc",
+    "cloud/mock_device_management_service.h",
+    "cloud/mock_signing_service.cc",
+    "cloud/mock_signing_service.h",
+    "cloud/mock_user_cloud_policy_store.cc",
+    "cloud/mock_user_cloud_policy_store.h",
+    "cloud/policy_builder.cc",
+    "cloud/policy_builder.h",
+    "configuration_policy_provider_test.cc",
+    "configuration_policy_provider_test.h",
+    "fake_async_policy_loader.cc",
+    "fake_async_policy_loader.h",
+    "mock_configuration_policy_provider.cc",
+    "mock_configuration_policy_provider.h",
+    "mock_policy_service.cc",
+    "mock_policy_service.h",
+    "policy_test_utils.cc",
+    "policy_test_utils.h",
+    "preferences_mock_mac.cc",
+    "preferences_mock_mac.h",
+    "remote_commands/test_remote_command_job.cc",
+    "remote_commands/test_remote_command_job.h",
+    "remote_commands/testing_remote_commands_server.cc",
+    "remote_commands/testing_remote_commands_server.h",
+  ]
+
+  if (is_chromeos) {
+    sources -= [
       "cloud/mock_user_cloud_policy_store.cc",
       "cloud/mock_user_cloud_policy_store.h",
-      "cloud/policy_builder.cc",
-      "cloud/policy_builder.h",
-      "configuration_policy_provider_test.cc",
-      "configuration_policy_provider_test.h",
-      "fake_async_policy_loader.cc",
-      "fake_async_policy_loader.h",
-      "mock_configuration_policy_provider.cc",
-      "mock_configuration_policy_provider.h",
-      "mock_policy_service.cc",
-      "mock_policy_service.h",
-      "policy_test_utils.cc",
-      "policy_test_utils.h",
-      "preferences_mock_mac.cc",
-      "preferences_mock_mac.h",
-      "remote_commands/test_remote_command_job.cc",
-      "remote_commands/test_remote_command_job.h",
-      "remote_commands/testing_remote_commands_server.cc",
-      "remote_commands/testing_remote_commands_server.h",
-    ]
-
-    if (is_chromeos) {
-      sources -= [
-        "cloud/mock_user_cloud_policy_store.cc",
-        "cloud/mock_user_cloud_policy_store.h",
-      ]
-    }
-
-    public_deps = [
-      ":common",
-      "//base",
-
-      # Explicitly link in the generated policy target into the test support
-      # so it will be linked to dependent targets. Otherwise in component
-      # build, it will be hidden inside the policy component.
-      "//components/policy:generated",
-      "//components/policy/proto",
-      "//crypto",
-      "//net",
-      "//testing/gmock",
-      "//testing/gtest",
     ]
   }
 
-  source_set("unit_tests") {
-    testonly = true
-    sources = [
-      "//extensions/features",
-      "cloud/cloud_policy_client_unittest.cc",
-      "cloud/cloud_policy_core_unittest.cc",
-      "cloud/cloud_policy_manager_unittest.cc",
-      "cloud/cloud_policy_refresh_scheduler_unittest.cc",
-      "cloud/cloud_policy_service_unittest.cc",
-      "cloud/cloud_policy_validator_unittest.cc",
-      "cloud/device_management_service_unittest.cc",
-      "cloud/policy_header_io_helper_unittest.cc",
-      "cloud/policy_header_service_unittest.cc",
-      "cloud/user_info_fetcher_unittest.cc",
-      "generate_policy_source_unittest.cc",
-      "policy_bundle_unittest.cc",
-      "policy_loader_ios_unittest.mm",
-      "policy_loader_mac_unittest.cc",
-      "policy_loader_win_unittest.cc",
-      "policy_map_unittest.cc",
-      "policy_service_impl_unittest.cc",
-      "policy_statistics_collector_unittest.cc",
-      "remote_commands/remote_commands_queue_unittest.cc",
-      "remote_commands/remote_commands_service_unittest.cc",
-      "schema_map_unittest.cc",
-      "schema_registry_tracking_policy_provider_unittest.cc",
-      "schema_registry_unittest.cc",
-      "schema_unittest.cc",
-    ]
-    if (is_win || is_chromeos) {
-      sources += [
-        "preg_parser_unittest.cc",
-        "registry_dict_unittest.cc",
-      ]
-    }
-    if (is_chromeos) {
-      sources += [ "proxy_policy_provider_unittest.cc" ]
-    } else {
-      sources += [
-        "cloud/user_cloud_policy_manager_unittest.cc",
-        "cloud/user_cloud_policy_store_unittest.cc",
-      ]
-    }
-    if (!is_android) {
-      sources += [ "async_policy_provider_unittest.cc" ]
-    }
-    if (!is_android && !is_ios) {
-      sources += [
-        "cloud/component_cloud_policy_service_unittest.cc",
-        "cloud/component_cloud_policy_store_unittest.cc",
-        "cloud/component_cloud_policy_updater_unittest.cc",
-        "cloud/external_policy_data_fetcher_unittest.cc",
-        "cloud/external_policy_data_updater_unittest.cc",
-        "cloud/resource_cache_unittest.cc",
-        "config_dir_policy_loader_unittest.cc",
-      ]
-    }
-    if (is_mac || is_ios) {
-      sources += [ "mac_util_unittest.cc" ]
-    }
-    if (is_win || is_chromeos) {
-      # Needed by policy_loader_win_unittest.cc and preg_parser_unittest.cc
-      data = [
-        "//chrome/test/data/policy/",
-      ]
-    }
+  public_deps = [
+    ":common",
+    "//base",
 
-    deps = [
-      ":test_support",
-      "//base",
-      "//base/test:test_support",
-      "//components/policy:generated",
-      "//components/prefs:test_support",
-      "//extensions/features",
-      "//google_apis",
-      "//net:test_support",
-      "//testing/gmock",
-      "//testing/gtest",
+    # Explicitly link in the generated policy target into the test support
+    # so it will be linked to dependent targets. Otherwise in component
+    # build, it will be hidden inside the policy component.
+    "//components/policy:generated",
+    "//components/policy/proto",
+    "//crypto",
+    "//net",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "//extensions/features",
+    "cloud/cloud_policy_client_unittest.cc",
+    "cloud/cloud_policy_core_unittest.cc",
+    "cloud/cloud_policy_manager_unittest.cc",
+    "cloud/cloud_policy_refresh_scheduler_unittest.cc",
+    "cloud/cloud_policy_service_unittest.cc",
+    "cloud/cloud_policy_validator_unittest.cc",
+    "cloud/device_management_service_unittest.cc",
+    "cloud/policy_header_io_helper_unittest.cc",
+    "cloud/policy_header_service_unittest.cc",
+    "cloud/user_info_fetcher_unittest.cc",
+    "generate_policy_source_unittest.cc",
+    "policy_bundle_unittest.cc",
+    "policy_loader_ios_unittest.mm",
+    "policy_loader_mac_unittest.cc",
+    "policy_loader_win_unittest.cc",
+    "policy_map_unittest.cc",
+    "policy_service_impl_unittest.cc",
+    "policy_statistics_collector_unittest.cc",
+    "remote_commands/remote_commands_queue_unittest.cc",
+    "remote_commands/remote_commands_service_unittest.cc",
+    "schema_map_unittest.cc",
+    "schema_registry_tracking_policy_provider_unittest.cc",
+    "schema_registry_unittest.cc",
+    "schema_unittest.cc",
+  ]
+  if (is_win || is_chromeos) {
+    sources += [
+      "preg_parser_unittest.cc",
+      "registry_dict_unittest.cc",
     ]
   }
+  if (is_chromeos) {
+    sources += [ "proxy_policy_provider_unittest.cc" ]
+  } else {
+    sources += [
+      "cloud/user_cloud_policy_manager_unittest.cc",
+      "cloud/user_cloud_policy_store_unittest.cc",
+    ]
+  }
+  if (!is_android) {
+    sources += [ "async_policy_provider_unittest.cc" ]
+  }
+  if (!is_android && !is_ios) {
+    sources += [
+      "cloud/component_cloud_policy_service_unittest.cc",
+      "cloud/component_cloud_policy_store_unittest.cc",
+      "cloud/component_cloud_policy_updater_unittest.cc",
+      "cloud/external_policy_data_fetcher_unittest.cc",
+      "cloud/external_policy_data_updater_unittest.cc",
+      "cloud/resource_cache_unittest.cc",
+      "config_dir_policy_loader_unittest.cc",
+    ]
+  }
+  if (is_mac || is_ios) {
+    sources += [ "mac_util_unittest.cc" ]
+  }
+  if (is_win || is_chromeos) {
+    # Needed by policy_loader_win_unittest.cc and preg_parser_unittest.cc
+    data = [
+      "//chrome/test/data/policy/",
+    ]
+  }
+
+  deps = [
+    ":test_support",
+    "//base",
+    "//base/test:test_support",
+    "//components/policy:generated",
+    "//components/prefs:test_support",
+    "//extensions/features",
+    "//google_apis",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
 }
diff --git a/components/search_engines/BUILD.gn b/components/search_engines/BUILD.gn
index 1de926d..88e51a9 100644
--- a/components/search_engines/BUILD.gn
+++ b/components/search_engines/BUILD.gn
@@ -83,7 +83,7 @@
     "//url",
   ]
 
-  if (enable_configuration_policy) {
+  if (!is_ios) {
     sources += [
       "default_search_policy_handler.cc",
       "default_search_policy_handler.h",
@@ -140,7 +140,7 @@
     "//url",
   ]
 
-  if (enable_configuration_policy) {
+  if (!is_ios) {
     sources += [ "default_search_policy_handler_unittest.cc" ]
 
     deps += [
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index b3b524c..67692bb 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -623,7 +623,7 @@
     ]
   }
 
-  if (enable_configuration_policy) {
+  if (!is_ios) {
     sources += [
       "driver/sync_policy_handler.cc",
       "driver/sync_policy_handler.h",
@@ -990,7 +990,7 @@
     sources += [ "engine_impl/loopback_server/loopback_server_unittest.cc" ]
   }
 
-  if (enable_configuration_policy) {
+  if (!is_ios) {
     sources += [ "driver/sync_policy_handler_unittest.cc" ]
     deps += [
       "//components/policy:generated",
diff --git a/components/sync_preferences/BUILD.gn b/components/sync_preferences/BUILD.gn
index f998255..d04fbd8 100644
--- a/components/sync_preferences/BUILD.gn
+++ b/components/sync_preferences/BUILD.gn
@@ -26,10 +26,7 @@
     "//components/sync",
   ]
 
-  if (enable_configuration_policy) {
-    # This define is only used for compiling the .cc files in this target.
-    defines = [ "SYNC_PREFERENCES_USE_POLICY" ]
-
+  if (!is_ios) {
     deps += [ "//components/policy/core/browser" ]
   }
 }
diff --git a/components/sync_preferences/pref_service_syncable_factory.cc b/components/sync_preferences/pref_service_syncable_factory.cc
index be3377e..2d314f5 100644
--- a/components/sync_preferences/pref_service_syncable_factory.cc
+++ b/components/sync_preferences/pref_service_syncable_factory.cc
@@ -5,13 +5,14 @@
 #include "components/sync_preferences/pref_service_syncable_factory.h"
 
 #include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/default_pref_store.h"
 #include "components/prefs/pref_notifier_impl.h"
 #include "components/prefs/pref_value_store.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 
-#if defined(SYNC_PREFERENCES_USE_POLICY)
+#if !defined(OS_IOS)
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/browser/configuration_policy_pref_store.h"
 #include "components/policy/core/common/policy_service.h"  // nogncheck
@@ -27,7 +28,7 @@
 void PrefServiceSyncableFactory::SetManagedPolicies(
     policy::PolicyService* service,
     policy::BrowserPolicyConnector* connector) {
-#if defined(SYNC_PREFERENCES_USE_POLICY)
+#if !defined(OS_IOS)
   set_managed_prefs(new policy::ConfigurationPolicyPrefStore(
       service, connector->GetHandlerList(), policy::POLICY_LEVEL_MANDATORY));
 #else
@@ -38,7 +39,7 @@
 void PrefServiceSyncableFactory::SetRecommendedPolicies(
     policy::PolicyService* service,
     policy::BrowserPolicyConnector* connector) {
-#if defined(SYNC_PREFERENCES_USE_POLICY)
+#if !defined(OS_IOS)
   set_recommended_prefs(new policy::ConfigurationPolicyPrefStore(
       service, connector->GetHandlerList(), policy::POLICY_LEVEL_RECOMMENDED));
 #else
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index a76a59f98..2b211b4 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -37,6 +37,7 @@
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/interfaces/service.mojom.h"
 #include "services/service_manager/runner/common/client_util.h"
+#include "services/service_manager/runner/host/in_process_native_runner.h"
 #include "services/service_manager/service_manager.h"
 
 namespace content {
@@ -133,22 +134,6 @@
   DISALLOW_COPY_AND_ASSIGN(BuiltinManifestProvider);
 };
 
-class NullNativeRunnerFactory : public service_manager::NativeRunnerFactory {
- public:
-  NullNativeRunnerFactory() {}
-  ~NullNativeRunnerFactory() override {}
-
-  std::unique_ptr<service_manager::NativeRunner> Create(
-      const base::FilePath& service_path) override {
-    LOG(ERROR) << "Attempting to run unsupported native service: "
-               << service_path.value();
-    return nullptr;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NullNativeRunnerFactory);
-};
-
 }  // namespace
 
 // State which lives on the IO thread and drives the ServiceManager.
@@ -189,10 +174,12 @@
     manifest_provider_ = std::move(manifest_provider);
 
     base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
+    std::unique_ptr<service_manager::NativeRunnerFactory> native_runner_factory(
+        new service_manager::InProcessNativeRunnerFactory(blocking_pool));
     catalog_.reset(
         new catalog::Catalog(blocking_pool, nullptr, manifest_provider_.get()));
     service_manager_.reset(new service_manager::ServiceManager(
-        base::MakeUnique<NullNativeRunnerFactory>(), catalog_->TakeService()));
+        std::move(native_runner_factory), catalog_->TakeService()));
 
     service_manager::mojom::ServiceRequest request =
         service_manager_->StartEmbedderService(mojom::kBrowserServiceName);
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index b62a031..e3aee0f 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -600,7 +600,7 @@
 RenderThreadImpl::RenderThreadImpl(
     const InProcessChildThreadParams& params,
     std::unique_ptr<blink::scheduler::RendererScheduler> scheduler,
-    scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue)
+    const scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue)
     : ChildThreadImpl(Options::Builder()
                           .InBrowserProcess(params)
                           .AutoStartServiceManagerConnection(false)
@@ -636,7 +636,7 @@
 }
 
 void RenderThreadImpl::Init(
-    scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue) {
   TRACE_EVENT0("startup", "RenderThreadImpl::Init");
 
   base::trace_event::TraceLog::GetInstance()->SetThreadSortIndex(
@@ -1231,7 +1231,7 @@
 }
 
 void RenderThreadImpl::InitializeWebKit(
-    scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue) {
   DCHECK(!blink_platform_impl_);
 
   const base::CommandLine& command_line =
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index de372d0c..3b38f52 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -486,7 +486,7 @@
   RenderThreadImpl(
       const InProcessChildThreadParams& params,
       std::unique_ptr<blink::scheduler::RendererScheduler> scheduler,
-      scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue);
+      const scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue);
   RenderThreadImpl(
       std::unique_ptr<base::MessageLoop> main_message_loop,
       std::unique_ptr<blink::scheduler::RendererScheduler> scheduler);
@@ -517,12 +517,13 @@
 
   void ClearMemory();
 
-  void Init(scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue);
+  void Init(
+      const scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue);
 
   void InitializeCompositorThread();
 
   void InitializeWebKit(
-      scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue);
+      const scoped_refptr<base::SingleThreadTaskRunner>& resource_task_queue);
 
   void OnTransferBitmap(const SkBitmap& bitmap, int resource_id);
   void OnGetAccessibilityTree();
diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc
index eef795a..1c69c0a 100644
--- a/content/renderer/render_thread_impl_browsertest.cc
+++ b/content/renderer/render_thread_impl_browsertest.cc
@@ -127,13 +127,6 @@
   ~RenderThreadImplForTest() override {}
 };
 
-class DummyListener : public IPC::Listener {
- public:
-  ~DummyListener() override {}
-
-  bool OnMessageReceived(const IPC::Message& message) override { return true; }
-};
-
 #if defined(COMPILER_MSVC)
 #pragma warning(pop)
 #endif
@@ -181,7 +174,7 @@
     browser_threads_.reset(
         new TestBrowserThreadBundle(TestBrowserThreadBundle::IO_MAINLOOP));
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
-        BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
+        base::ThreadTaskRunnerHandle::Get();
 
     InitializeMojo();
     ipc_support_.reset(new mojo::edk::test::ScopedIPCSupport(io_task_runner));
@@ -195,11 +188,10 @@
     IPC::mojom::ChannelBootstrapPtr channel_bootstrap;
     child_connection_->GetRemoteInterfaces()->GetInterface(&channel_bootstrap);
 
-    dummy_listener_.reset(new DummyListener);
     channel_ = IPC::ChannelProxy::Create(
         IPC::ChannelMojo::CreateServerFactory(
             channel_bootstrap.PassInterface().PassHandle(), io_task_runner),
-        dummy_listener_.get(), io_task_runner);
+        nullptr, io_task_runner);
 
     mock_process_.reset(new MockRenderProcess);
     test_task_counter_ = make_scoped_refptr(new TestTaskCounter());
@@ -239,7 +231,6 @@
   std::unique_ptr<mojo::edk::test::ScopedIPCSupport> ipc_support_;
   std::unique_ptr<TestServiceManagerContext> shell_context_;
   std::unique_ptr<ChildConnection> child_connection_;
-  std::unique_ptr<DummyListener> dummy_listener_;
   std::unique_ptr<IPC::ChannelProxy> channel_;
 
   std::unique_ptr<MockRenderProcess> mock_process_;
diff --git a/content/test/data/media/getusermedia-depth-capture.html b/content/test/data/media/getusermedia-depth-capture.html
index 0dc5953c..1ffde99f 100644
--- a/content/test/data/media/getusermedia-depth-capture.html
+++ b/content/test/data/media/getusermedia-depth-capture.html
@@ -144,9 +144,9 @@
     canvas.width = 96;
     canvas.height = 96;
     var gl = canvas.getContext('webgl');
-    if(!gl)
+    if (!gl)
       return error({name:"WebGL is not available."});
-    if(!gl.getExtension("OES_texture_float"))
+    if (!gl.getExtension("OES_texture_float"))
       return error({name:"OES_texture_float extension is not available."});
     return testVideoToTexture(gl, video, gl.RGBA, gl.RGBA, gl.FLOAT,
                               readAndVerifyRGBA32F, success, error);
@@ -158,7 +158,7 @@
     canvas.width = 96;
     canvas.height = 96;
     var gl = canvas.getContext('webgl');
-    if(!gl)
+    if (!gl)
       return error({name:"WebGL is not available."});
     return testVideoToTexture(gl, video, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
                               readAndVerifyRGBA8, success, error);
@@ -170,9 +170,10 @@
     canvas.width = 96;
     canvas.height = 96;
     var gl = canvas.getContext('webgl2');
-    if(!gl)
+    if (!gl)
       return error({name:"WebGL2 is not available."});
-    var color_buffer_float_ext = gl.getExtension('EXT_color_buffer_float');
+    if (!gl.getExtension('EXT_color_buffer_float'))
+      return error({name:"EXT_color_buffer_float extension is not available."});
     return testVideoToTexture(gl, video, gl.R32F, gl.RED, gl.FLOAT,
                               readAndVerifyR32F, success, error);
   }
diff --git a/net/quic/chromium/quic_test_packet_maker.cc b/net/quic/chromium/quic_test_packet_maker.cc
index ac82f5b..cd0941a5 100644
--- a/net/quic/chromium/quic_test_packet_maker.cc
+++ b/net/quic/chromium/quic_test_packet_maker.cc
@@ -25,6 +25,8 @@
       connection_id_(connection_id),
       clock_(clock),
       host_(host),
+      spdy_request_framer_(SpdyFramer::ENABLE_COMPRESSION),
+      spdy_response_framer_(SpdyFramer::ENABLE_COMPRESSION),
       perspective_(perspective) {}
 
 QuicTestPacketMaker::~QuicTestPacketMaker() {}
diff --git a/net/quic/core/quic_headers_stream.cc b/net/quic/core/quic_headers_stream.cc
index 7f890f6..8eca9f0 100644
--- a/net/quic/core/quic_headers_stream.cc
+++ b/net/quic/core/quic_headers_stream.cc
@@ -327,6 +327,7 @@
       supports_push_promise_(session->perspective() == Perspective::IS_CLIENT),
       cur_max_timestamp_(QuicTime::Zero()),
       prev_max_timestamp_(QuicTime::Zero()),
+      spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
       spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
   spdy_framer_.set_visitor(spdy_framer_visitor_.get());
   spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
diff --git a/net/quic/core/quic_headers_stream_test.cc b/net/quic/core/quic_headers_stream_test.cc
index 3651973..65727a1 100644
--- a/net/quic/core/quic_headers_stream_test.cc
+++ b/net/quic/core/quic_headers_stream_test.cc
@@ -236,7 +236,8 @@
     headers_[":version"] = "HTTP/1.1";
     headers_[":status"] = "200 Ok";
     headers_["content-length"] = "11";
-    framer_ = std::unique_ptr<SpdyFramer>(new SpdyFramer);
+    framer_ = std::unique_ptr<SpdyFramer>(
+        new SpdyFramer(SpdyFramer::ENABLE_COMPRESSION));
     framer_->set_visitor(&visitor_);
     EXPECT_EQ(version(), session_.connection()->version());
     EXPECT_TRUE(headers_stream_ != nullptr);
diff --git a/net/quic/core/spdy_utils.cc b/net/quic/core/spdy_utils.cc
index 366bb88..f551696 100644
--- a/net/quic/core/spdy_utils.cc
+++ b/net/quic/core/spdy_utils.cc
@@ -28,7 +28,7 @@
 string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
   size_t length = SpdyFramer::GetSerializedLength(&headers);
   SpdyFrameBuilder builder(length);
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
   framer.SerializeHeaderBlockWithoutCompression(&builder, headers);
   SpdySerializedFrame block(builder.take());
   return string(block.data(), length);
@@ -39,7 +39,7 @@
                              uint32_t data_len,
                              int64_t* content_length,
                              SpdyHeaderBlock* headers) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   if (!framer.ParseHeaderBlockInBuffer(data, data_len, headers) ||
       headers->empty()) {
     return false;  // Headers were invalid.
@@ -90,7 +90,7 @@
                               uint32_t data_len,
                               size_t* final_byte_offset,
                               SpdyHeaderBlock* trailers) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   if (!framer.ParseHeaderBlockInBuffer(data, data_len, trailers) ||
       trailers->empty()) {
     DVLOG(1) << "Request Trailers are invalid.";
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc
index ead180fc..a1479c0b 100644
--- a/net/spdy/buffered_spdy_framer.cc
+++ b/net/spdy/buffered_spdy_framer.cc
@@ -20,7 +20,8 @@
 }  // namespace
 
 BufferedSpdyFramer::BufferedSpdyFramer()
-    : visitor_(NULL),
+    : spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
+      visitor_(NULL),
       header_buffer_valid_(false),
       header_stream_id_(SpdyFramer::kInvalidStream),
       frames_received_(0) {}
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc
index ba6a67d..82a6637 100644
--- a/net/spdy/buffered_spdy_framer_unittest.cc
+++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -179,7 +179,7 @@
 class BufferedSpdyFramerTest : public PlatformTest {};
 
 TEST_F(BufferedSpdyFramerTest, OnSetting) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_ir;
   settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false, 2);
   settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false, 3);
diff --git a/net/spdy/spdy_deframer_visitor_test.cc b/net/spdy/spdy_deframer_visitor_test.cc
index 7498303..4652049 100644
--- a/net/spdy/spdy_deframer_visitor_test.cc
+++ b/net/spdy/spdy_deframer_visitor_test.cc
@@ -31,7 +31,9 @@
 
 class SpdyDeframerVisitorTest : public ::testing::Test {
  protected:
-  SpdyDeframerVisitorTest() {
+  SpdyDeframerVisitorTest()
+      : encoder_(SpdyFramer::ENABLE_COMPRESSION),
+        decoder_(SpdyFramer::ENABLE_COMPRESSION) {
     decoder_.set_process_single_input_frame(true);
     auto collector = MakeUnique<DeframerCallbackCollector>(&collected_frames_);
     auto log_and_collect =
diff --git a/net/spdy/spdy_frame_builder_test.cc b/net/spdy/spdy_frame_builder_test.cc
index 921df698..624a534 100644
--- a/net/spdy/spdy_frame_builder_test.cc
+++ b/net/spdy/spdy_frame_builder_test.cc
@@ -27,7 +27,7 @@
   // Create an empty SETTINGS frame both via framer and manually via builder.
   // The one created via builder is initially given the incorrect length, but
   // then is corrected via RewriteLength().
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_ir;
   SpdySerializedFrame expected(framer.SerializeSettings(settings_ir));
   SpdyFrameBuilder builder(expected.size() + 1);
@@ -42,7 +42,7 @@
 TEST(SpdyFrameBuilderTest, OverwriteFlags) {
   // Create a HEADERS frame both via framer and manually via builder with
   // different flags set, then make them match using OverwriteFlags().
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyHeadersIR headers_ir(1);
   SpdySerializedFrame expected(framer.SerializeHeaders(headers_ir));
   SpdyFrameBuilder builder(expected.size());
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 501f4a45..5d724f84 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -153,13 +153,14 @@
   return true;
 }
 
-SpdyFramer::SpdyFramer(SpdyFramer::DecoderAdapterFactoryFn adapter_factory)
+SpdyFramer::SpdyFramer(SpdyFramer::DecoderAdapterFactoryFn adapter_factory,
+                       CompressionOption option)
     : current_frame_buffer_(kControlFrameBufferSize),
       expect_continuation_(0),
       visitor_(NULL),
       debug_visitor_(NULL),
       header_handler_(nullptr),
-      enable_compression_(true),
+      compression_option_(option),
       probable_http_response_(false),
       end_stream_when_done_(false) {
   // TODO(bnc): The way kMaxControlFrameSize is currently interpreted, it
@@ -174,7 +175,8 @@
   }
 }
 
-SpdyFramer::SpdyFramer() : SpdyFramer(&DecoderAdapterFactory) {}
+SpdyFramer::SpdyFramer(CompressionOption option)
+    : SpdyFramer(&DecoderAdapterFactory, option) {}
 
 SpdyFramer::~SpdyFramer() {
 }
@@ -1782,7 +1784,7 @@
       is_first_frame_(true),
       has_next_frame_(true) {
   encoder_ = framer_->GetHpackEncoder()->EncodeHeaderSet(
-      headers_ir_->header_block(), framer_->enable_compression_);
+      headers_ir_->header_block(), framer_->compression_enabled());
 }
 
 SpdyFramer::SpdyHeaderFrameIterator::~SpdyHeaderFrameIterator() {}
@@ -2004,7 +2006,7 @@
   }
 
   string hpack_encoding;
-  if (enable_compression_) {
+  if (compression_enabled()) {
     GetHpackEncoder()->EncodeHeaderSet(headers.header_block(), &hpack_encoding);
   } else {
     GetHpackEncoder()->EncodeHeaderSetWithoutCompression(headers.header_block(),
@@ -2081,7 +2083,7 @@
   }
 
   string hpack_encoding;
-  if (enable_compression_) {
+  if (compression_enabled()) {
     GetHpackEncoder()->EncodeHeaderSet(push_promise.header_block(),
                                        &hpack_encoding);
   } else {
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index 17202c0..79f66a5 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -319,6 +319,11 @@
     LAST_ERROR,  // Must be the last entry in the enum.
   };
 
+  enum CompressionOption {
+    ENABLE_COMPRESSION,
+    DISABLE_COMPRESSION,
+  };
+
   // Typedef for a function used to create SpdyFramerDecoderAdapter's.
   // Defined in support of evaluating an alternate HTTP/2 decoder.
   typedef std::unique_ptr<SpdyFramerDecoderAdapter> (*DecoderAdapterFactoryFn)(
@@ -338,12 +343,12 @@
   // Retrieve serialized length of SpdyHeaderBlock.
   static size_t GetSerializedLength(const SpdyHeaderBlock* headers);
 
-  SpdyFramer();
+  explicit SpdyFramer(CompressionOption option);
 
   // Used recursively from the above constructor in order to support
   // instantiating a SpdyFramerDecoderAdapter selected via flags or some other
   // means.
-  explicit SpdyFramer(DecoderAdapterFactoryFn adapter_factory);
+  SpdyFramer(DecoderAdapterFactoryFn adapter_factory, CompressionOption option);
 
   virtual ~SpdyFramer();
 
@@ -475,10 +480,9 @@
   // Serialize a frame of unknown type.
   SpdySerializedFrame SerializeFrame(const SpdyFrameIR& frame);
 
-  // For ease of testing and experimentation we can tweak compression on/off.
-  bool enable_compression() const { return enable_compression_; }
-  void set_enable_compression(bool value) {
-    enable_compression_ = value;
+  // Returns whether this SpdyFramer will compress header blocks using HPACK.
+  bool compression_enabled() const {
+    return compression_option_ == ENABLE_COMPRESSION;
   }
 
   void SetHpackIndexingPolicy(HpackEncoder::IndexingPolicy policy) {
@@ -766,7 +770,7 @@
   uint8_t current_frame_flags_;
 
   // Determines whether HPACK compression is used.
-  bool enable_compression_;
+  const CompressionOption compression_option_;
 
   // On the first read, we check to see if the data starts with HTTP.
   // If it does, we likely have an HTTP response.   This isn't guaranteed
diff --git a/net/spdy/spdy_framer_decoder_adapter.cc b/net/spdy/spdy_framer_decoder_adapter.cc
index 83a479e7..b68f04a 100644
--- a/net/spdy/spdy_framer_decoder_adapter.cc
+++ b/net/spdy/spdy_framer_decoder_adapter.cc
@@ -184,7 +184,10 @@
 
  public:
   explicit NestedSpdyFramerDecoder(SpdyFramer* outer)
-      : framer_(nullptr), outer_(outer) {
+      : framer_(nullptr,
+                outer->compression_enabled() ? SpdyFramer::ENABLE_COMPRESSION
+                                             : SpdyFramer::DISABLE_COMPRESSION),
+        outer_(outer) {
     DVLOG(1) << PRETTY_THIS;
   }
   ~NestedSpdyFramerDecoder() override { DVLOG(1) << PRETTY_THIS; }
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index 27a18d4c..470707d 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -69,8 +69,7 @@
     CHECK_EQ(frame.size(), framer->ProcessInput(frame.data(), frame.size()));
     CHECK_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer->state());
     framer->set_visitor(nullptr);
-    SpdyFramer serializer;
-    serializer.set_enable_compression(false);
+    SpdyFramer serializer(SpdyFramer::DISABLE_COMPRESSION);
     return serializer.SerializeFrame(visitor.GetFrame());
   }
 
@@ -272,8 +271,8 @@
   // are too long can spill over into CONTINUATION frames.
   static const size_t kDefaultHeaderBufferSize = 16 * 1024 * 1024;
 
-  TestSpdyVisitor()
-      : use_compression_(false),
+  explicit TestSpdyVisitor(SpdyFramer::CompressionOption option)
+      : framer_(option),
         error_count_(0),
         headers_frame_count_(0),
         push_promise_frame_count_(0),
@@ -494,7 +493,6 @@
 
   // Convenience function which runs a framer simulation with particular input.
   void SimulateInFramer(const unsigned char* input, size_t size) {
-    framer_.set_enable_compression(use_compression_);
     framer_.set_visitor(this);
     size_t input_remaining = size;
     const char* input_ptr = reinterpret_cast<const char*>(input);
@@ -550,7 +548,6 @@
   }
 
   SpdyFramer framer_;
-  bool use_compression_;
 
   // Counters from the visitor callbacks.
   int error_count_;
@@ -677,8 +674,7 @@
 
 // Test that we can encode and decode a SpdyHeaderBlock in serialized form.
 TEST_P(SpdyFramerTest, HeaderBlockInBuffer) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
 
   // Encode the header block into a Headers frame.
   SpdyHeadersIR headers(1);
@@ -687,8 +683,7 @@
   headers.SetHeader("cookie", "key1=value1; key2=value2");
   SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders(&framer, headers));
 
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(reinterpret_cast<unsigned char*>(frame.data()),
                            frame.size());
 
@@ -698,8 +693,7 @@
 
 // Test that if there's not a full frame, we fail to parse it.
 TEST_P(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
 
   // Encode the header block into a Headers frame.
   SpdyHeadersIR headers(1);
@@ -707,8 +701,7 @@
   headers.SetHeader("gamma", "charlie");
   SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders(&framer, headers));
 
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(reinterpret_cast<unsigned char*>(frame.data()),
                            frame.size() - 2);
 
@@ -719,8 +712,7 @@
 // Test that we treat incoming upper-case or mixed-case header values as
 // malformed.
 TEST_P(SpdyFramerTest, RejectUpperCaseHeaderBlockValue) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
 
   SpdyFrameBuilder frame(1024);
   frame.BeginNewFrame(framer, HEADERS, 0, 1);
@@ -754,8 +746,7 @@
 // Test that we can encode and decode stream dependency values in a header
 // frame.
 TEST_P(SpdyFramerTest, HeaderStreamDependencyValues) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
 
   const SpdyStreamId parent_stream_id_test_array[] = {0, 3};
   for (SpdyStreamId parent_stream_id : parent_stream_id_test_array) {
@@ -768,8 +759,7 @@
       SpdySerializedFrame frame(
           SpdyFramerPeer::SerializeHeaders(&framer, headers));
 
-      TestSpdyVisitor visitor;
-      visitor.use_compression_ = false;
+      TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
       visitor.SimulateInFramer(reinterpret_cast<unsigned char*>(frame.data()),
                                frame.size());
 
@@ -784,7 +774,7 @@
 // advertised max size, we do not set an error in ProcessInput.
 TEST_P(SpdyFramerTest, AcceptMaxFrameSizeSetting) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // DATA frame with maximum allowed payload length.
@@ -809,7 +799,7 @@
 // advertised max size, we set an error of SPDY_INVALID_CONTROL_FRAME_SIZE.
 TEST_P(SpdyFramerTest, ExceedMaxFrameSizeSetting) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // DATA frame with too large payload length.
@@ -835,7 +825,7 @@
 // payload length, we set an error of SPDY_INVALID_PADDING
 TEST_P(SpdyFramerTest, OversizedDataPaddingError) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // DATA frame with invalid padding length.
@@ -870,7 +860,7 @@
 // payload length, we do not set an error of SPDY_INVALID_PADDING
 TEST_P(SpdyFramerTest, CorrectlySizedDataPaddingNoError) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // DATA frame with valid Padding length
@@ -905,7 +895,7 @@
 // payload length, we set an error of SPDY_INVALID_PADDING
 TEST_P(SpdyFramerTest, OversizedHeadersPaddingError) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // HEADERS frame with invalid padding length.
@@ -937,7 +927,7 @@
 // than the payload length, we do not set an error of SPDY_INVALID_PADDING
 TEST_P(SpdyFramerTest, CorrectlySizedHeadersPaddingNoError) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // HEADERS frame with invalid Padding length
@@ -964,7 +954,7 @@
 // (but don't crash).
 TEST_P(SpdyFramerTest, DataWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   const char bytes[] = "hello";
@@ -983,7 +973,7 @@
 // (but don't crash).
 TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyHeadersIR headers(0);
@@ -1002,7 +992,7 @@
 // (but don't crash).
 TEST_P(SpdyFramerTest, PriorityWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyPriorityIR priority_ir(0, 1, 16, true);
@@ -1020,7 +1010,7 @@
 // (but don't crash).
 TEST_P(SpdyFramerTest, RstStreamWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyRstStreamIR rst_stream_ir(0, RST_STREAM_PROTOCOL_ERROR);
@@ -1038,7 +1028,7 @@
 // we signal an error (but don't crash).
 TEST_P(SpdyFramerTest, SettingsWithStreamIdNotZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // Settings frame with invalid StreamID of 0x01
@@ -1065,7 +1055,7 @@
 // we signal an error (but don't crash).
 TEST_P(SpdyFramerTest, GoawayWithStreamIdNotZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // GOAWAY frame with invalid StreamID of 0x01
@@ -1093,7 +1083,7 @@
 // SPDY_INVALID_STREAM_ID.
 TEST_P(SpdyFramerTest, ContinuationWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyContinuationIR continuation(0);
@@ -1115,7 +1105,7 @@
 // SPDY_INVALID_STREAM_ID.
 TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyPushPromiseIR push_promise(0, 4);
@@ -1134,7 +1124,7 @@
 // signal SPDY_INVALID_STREAM_ID.
 TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyPushPromiseIR push_promise(3, 0);
@@ -1149,7 +1139,7 @@
 }
 
 TEST_P(SpdyFramerTest, DuplicateHeader) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
   // Frame builder with plentiful buffer size.
   SpdyFrameBuilder frame(1024);
   frame.BeginNewFrame(framer, HEADERS, 0, 3);
@@ -1163,7 +1153,6 @@
   frame.RewriteLength(framer);
 
   SpdyHeaderBlock new_headers;
-  framer.set_enable_compression(false);
   SpdySerializedFrame control_frame(frame.take());
   StringPiece serialized_headers = GetSerializedHeaders(control_frame, framer);
   // This should fail because duplicate headers are verboten by the spec.
@@ -1172,7 +1161,7 @@
 }
 
 TEST_P(SpdyFramerTest, MultiValueHeader) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
   // Frame builder with plentiful buffer size.
   SpdyFrameBuilder frame(1024);
   frame.BeginNewFrame(framer, HEADERS,
@@ -1191,11 +1180,9 @@
   // write the length
   frame.RewriteLength(framer);
 
-  framer.set_enable_compression(false);
   SpdySerializedFrame control_frame(frame.take());
 
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       control_frame.size());
@@ -1215,8 +1202,7 @@
   headers.SetHeader("content-length", "12");
   headers.SetHeader("x-empty-header", "");
 
-  SpdyFramer framer;
-  framer.set_enable_compression(true);
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySerializedFrame frame1(
       SpdyFramerPeer::SerializeHeaders(&framer, headers));
 }
@@ -1285,7 +1271,7 @@
   };
   // frame-format on
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kH2Input, sizeof(kH2Input));
 
   EXPECT_EQ(24, visitor.data_bytes_);
@@ -1335,7 +1321,7 @@
   };
   // frame-format on
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kH2Input, sizeof(kH2Input));
 
   EXPECT_EQ(0, visitor.error_count_);
@@ -1366,7 +1352,7 @@
   };
   // frame-format on
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kH2Input, sizeof(kH2Input));
 
   EXPECT_EQ(0, visitor.error_count_);
@@ -1381,9 +1367,7 @@
 // Verify we can decompress the stream even if handed over to the
 // framer 1 byte at a time.
 TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) {
-  SpdyFramer framer;
-
-  framer.set_enable_compression(true);
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   const char kHeader1[] = "header1";
   const char kHeader2[] = "header2";
@@ -1402,8 +1386,7 @@
   SpdySerializedFrame send_frame(framer.SerializeData(data_ir));
 
   // Run the inputs through the framer.
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = true;
+  TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION);
   const unsigned char* data;
   data = reinterpret_cast<const unsigned char*>(headers_frame.data());
   for (size_t idx = 0; idx < headers_frame.size(); ++idx) {
@@ -1426,7 +1409,7 @@
 }
 
 TEST_P(SpdyFramerTest, WindowUpdateFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySerializedFrame frame(
       framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 0x12345678)));
 
@@ -1443,7 +1426,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreateDataFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   {
     const char kDescription[] = "'hello' data frame, no FIN";
@@ -1645,7 +1628,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreateRstStream) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   {
     const char kDescription[] = "RST_STREAM frame";
@@ -1691,7 +1674,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreateSettings) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   {
     const char kDescription[] = "Network byte order SETTINGS frame";
@@ -1775,7 +1758,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreatePingFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   {
     const char kDescription[] = "PING frame";
@@ -1812,7 +1795,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreateGoAway) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   {
     const char kDescription[] = "GOAWAY frame";
@@ -1848,8 +1831,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
 
   {
     const char kDescription[] = "HEADERS frame, no FIN";
@@ -2100,8 +2082,7 @@
 // to workaround http://crbug.com/139744.
 #if !defined(USE_SYSTEM_ZLIB)
 TEST_P(SpdyFramerTest, CreateHeadersCompressed) {
-  SpdyFramer framer;
-  framer.set_enable_compression(true);
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   {
     SpdyHeadersIR headers_ir(1);
@@ -2115,7 +2096,7 @@
 #endif  // !defined(USE_SYSTEM_ZLIB)
 
 TEST_P(SpdyFramerTest, CreateWindowUpdate) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   {
     const char kDescription[] = "WINDOW_UPDATE frame";
@@ -2161,7 +2142,7 @@
 }
 
 TEST_P(SpdyFramerTest, SerializeBlocked) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   const char kDescription[] = "BLOCKED frame";
   const unsigned char kType =
@@ -2178,7 +2159,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreateBlocked) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   const char kDescription[] = "BLOCKED frame";
   const SpdyStreamId kStreamId = 3;
@@ -2194,8 +2175,7 @@
 TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) {
   {
     // Test framing PUSH_PROMISE without padding.
-    SpdyFramer framer;
-    framer.set_enable_compression(false);
+    SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
     const char kDescription[] = "PUSH_PROMISE frame without padding";
 
     // frame-format off
@@ -2229,8 +2209,7 @@
 
   {
     // Test framing PUSH_PROMISE with one byte of padding.
-    SpdyFramer framer;
-    framer.set_enable_compression(false);
+    SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
     const char kDescription[] = "PUSH_PROMISE frame with one byte of padding";
 
     // frame-format off
@@ -2266,8 +2245,7 @@
 
   {
     // Test framing PUSH_PROMISE with 177 bytes of padding.
-    SpdyFramer framer;
-    framer.set_enable_compression(false);
+    SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
     const char kDescription[] = "PUSH_PROMISE frame with 177 bytes of padding";
 
     // frame-format off
@@ -2324,7 +2302,7 @@
 
 // Regression test for https://crbug.com/464748.
 TEST_P(SpdyFramerTest, GetNumberRequiredContinuationFrames) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   EXPECT_EQ(1u, SpdyFramerPeer::GetNumberRequiredContinuationFrames(
                     &framer, 16383 + 16374));
   EXPECT_EQ(2u, SpdyFramerPeer::GetNumberRequiredContinuationFrames(
@@ -2336,8 +2314,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreateContinuationUncompressed) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
   const char kDescription[] = "CONTINUATION frame";
 
   // frame-format off
@@ -2380,7 +2357,7 @@
 // we signal an error (but don't crash).
 TEST_P(SpdyFramerTest, SendUnexpectedContinuation) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   // frame-format off
@@ -2419,8 +2396,7 @@
     // Test framing in a case such that a PUSH_PROMISE frame, with one byte of
     // padding, cannot hold all the data payload, which is overflowed to the
     // consecutive CONTINUATION frame.
-    SpdyFramer framer;
-    framer.set_enable_compression(false);
+    SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
     const char kDescription[] =
         "PUSH_PROMISE and CONTINUATION frames with one byte of padding";
 
@@ -2516,7 +2492,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreateAltSvc) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   const char kDescription[] = "ALTSVC frame";
   const char kType =
@@ -2541,7 +2517,7 @@
 }
 
 TEST_P(SpdyFramerTest, CreatePriority) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   const char kDescription[] = "PRIORITY frame";
   const unsigned char kFrameData[] = {
@@ -2564,14 +2540,13 @@
 }
 
 TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyHeadersIR headers_ir(1);
   headers_ir.SetHeader("alpha", "beta");
   headers_ir.SetHeader("gamma", "delta");
   SpdySerializedFrame control_frame(
       SpdyFramerPeer::SerializeHeaders(&framer, headers_ir));
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = true;
+  TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       control_frame.size());
@@ -2583,15 +2558,14 @@
 }
 
 TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyHeadersIR headers_ir(1);
   headers_ir.set_fin(true);
   headers_ir.SetHeader("alpha", "beta");
   headers_ir.SetHeader("gamma", "delta");
   SpdySerializedFrame control_frame(
       SpdyFramerPeer::SerializeHeaders(&framer, headers_ir));
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = true;
+  TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       control_frame.size());
@@ -2603,8 +2577,7 @@
 }
 
 TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
   SpdyHeadersIR headers(1);
   headers.set_padding_len(256);
 
@@ -2618,7 +2591,7 @@
   EXPECT_GT(control_frame.size(),
             TestSpdyVisitor::sent_control_frame_max_size());
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       control_frame.size());
@@ -2630,8 +2603,7 @@
 }
 
 TEST_P(SpdyFramerTest, MultipleContinuationFramesWithIterator) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
   auto headers = base::MakeUnique<SpdyHeadersIR>(1);
   headers->set_padding_len(256);
 
@@ -2650,7 +2622,7 @@
   EXPECT_EQ(headers_frame.size(),
             TestSpdyVisitor::sent_control_frame_max_size());
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(headers_frame.data()),
       headers_frame.size());
@@ -2692,8 +2664,7 @@
 }
 
 TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) {
-  SpdyFramer framer;
-  framer.set_enable_compression(false);
+  SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION);
   SpdyPushPromiseIR push_promise(1, 2);
   push_promise.set_padding_len(256);
 
@@ -2706,7 +2677,7 @@
   EXPECT_GT(control_frame.size(),
             TestSpdyVisitor::sent_control_frame_max_size());
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       control_frame.size());
@@ -2726,15 +2697,14 @@
       TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks;
   const size_t kBigValueSize = kHeaderBufferSize * 2;
   string big_value(kBigValueSize, 'x');
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyHeadersIR headers(1);
   headers.set_fin(true);
   headers.SetHeader("aa", big_value);
   SpdySerializedFrame control_frame(
       SpdyFramerPeer::SerializeHeaders(&framer, headers));
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION);
   visitor.set_header_buffer_size(kHeaderBufferSize);
-  visitor.use_compression_ = true;
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       control_frame.size());
@@ -2745,7 +2715,7 @@
 }
 
 TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   // Create a GoAway frame that has a few extra bytes at the end.
   // We create enough overhead to overflow the framer's control frame buffer.
   ASSERT_LE(SpdyFramerPeer::ControlFrameBufferSize(), 250u);
@@ -2769,7 +2739,7 @@
   const size_t pad_length =
       length + SpdyConstants::kFrameHeaderSize - sizeof(kH2FrameData);
   string pad(pad_length, 'A');
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
 
   visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
   visitor.SimulateInFramer(reinterpret_cast<const unsigned char*>(pad.c_str()),
@@ -2783,12 +2753,11 @@
 }
 
 TEST_P(SpdyFramerTest, ReadZeroLenSettingsFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_ir;
   SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir));
   SetFrameLength(&control_frame, 0);
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       framer.GetFrameHeaderSize());
@@ -2798,7 +2767,7 @@
 
 // Tests handling of SETTINGS frames with invalid length.
 TEST_P(SpdyFramerTest, ReadBogusLenSettingsFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_ir;
 
   // Add settings to more than fill the frame so that we don't get a buffer
@@ -2812,8 +2781,7 @@
   SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir));
   const size_t kNewLength = 8;
   SetFrameLength(&control_frame, kNewLength);
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       framer.GetFrameHeaderSize() + kNewLength);
@@ -2827,7 +2795,7 @@
 
 // Tests handling of SETTINGS frames larger than the frame buffer size.
 TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_ir;
   settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE,
                          false,  // persist
@@ -2844,8 +2812,7 @@
 
   SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir));
   EXPECT_LT(SpdyFramerPeer::ControlFrameBufferSize(), control_frame.size());
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
 
   // Read all at once.
   visitor.SimulateInFramer(
@@ -2874,7 +2841,7 @@
 
 // Tests handling of SETTINGS frame with duplicate entries.
 TEST_P(SpdyFramerTest, ReadDuplicateSettings) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x12,        // Length: 18
@@ -2889,8 +2856,7 @@
       0x00, 0x00, 0x00, 0x03,  //  Value: 3
   };
 
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
 
   // In HTTP/2, duplicate settings are allowed;
@@ -2902,7 +2868,7 @@
 
 // Tests handling of SETTINGS frame with a setting we don't recognize.
 TEST_P(SpdyFramerTest, ReadUnknownSettingsId) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x06,        // Length: 6
       0x04,                    //   Type: SETTINGS
@@ -2912,8 +2878,7 @@
       0x00, 0x00, 0x00, 0x02,  //  Value: 2
   };
 
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
 
   // In HTTP/2, we ignore unknown settings because of extensions.
@@ -2923,7 +2888,7 @@
 
 // Tests handling of SETTINGS frame with entries out of order.
 TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x12,        // Length: 18
       0x04,                    //   Type: SETTINGS
@@ -2937,8 +2902,7 @@
       0x00, 0x00, 0x00, 0x03,  //  Value: 3
   };
 
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData));
 
   // In HTTP/2, settings are allowed in any order.
@@ -2947,7 +2911,7 @@
 }
 
 TEST_P(SpdyFramerTest, ProcessSettingsAckFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   const unsigned char kFrameData[] = {
       0x00, 0x00, 0x00,        // Length: 0
@@ -2956,8 +2920,7 @@
       0x00, 0x00, 0x00, 0x00,  // Stream: 0
   };
 
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
 
   EXPECT_EQ(0, visitor.error_count_);
@@ -2970,7 +2933,7 @@
   const char data_payload[] = "hello";
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyDataIR data_ir(1, data_payload);
@@ -3024,10 +2987,10 @@
 }
 
 TEST_P(SpdyFramerTest, ReadWindowUpdate) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySerializedFrame control_frame(
       framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2)));
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame.data()),
       control_frame.size());
@@ -3036,13 +2999,12 @@
 }
 
 TEST_P(SpdyFramerTest, ReadCompressedPushPromise) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyPushPromiseIR push_promise(42, 57);
   push_promise.SetHeader("foo", "bar");
   push_promise.SetHeader("bar", "foofoo");
   SpdySerializedFrame frame(framer.SerializePushPromise(push_promise));
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = true;
+  TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION);
   visitor.SimulateInFramer(reinterpret_cast<unsigned char*>(frame.data()),
                            frame.size());
   EXPECT_EQ(42u, visitor.last_push_promise_stream_);
@@ -3092,8 +3054,7 @@
   };
   // frame-format on
 
-  SpdyFramer framer;
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kInput, sizeof(kInput));
 
   EXPECT_EQ(0, visitor.error_count_);
@@ -3148,8 +3109,8 @@
   };
   // frame-format on
 
-  SpdyFramer framer;
-  TestSpdyVisitor visitor;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kInput, sizeof(kInput));
 
   EXPECT_EQ(0, visitor.error_count_);
@@ -3196,8 +3157,8 @@
   };
   // frame-format on
 
-  SpdyFramer framer;
-  TestSpdyVisitor visitor;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kInput, sizeof(kInput));
 
   EXPECT_EQ(0, visitor.error_count_);
@@ -3237,8 +3198,8 @@
       0x67, 0x00, 0x06, 0x63,  //
   };
 
-  SpdyFramer framer;
-  TestSpdyVisitor visitor;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   // Assume the unknown frame is allowed
   visitor.on_unknown_frame_result_ = true;
   framer.set_visitor(&visitor);
@@ -3274,8 +3235,8 @@
       0x67, 0x00, 0x06, 0x63,  //
   };
 
-  SpdyFramer framer;
-  TestSpdyVisitor visitor;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   framer.set_visitor(&visitor);
   visitor.SimulateInFramer(kInput, sizeof(kInput));
 
@@ -3299,8 +3260,8 @@
       0x3d, 0x62, 0x61, 0x72,  //
   };
 
-  SpdyFramer framer;
-  TestSpdyVisitor visitor;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   framer.set_visitor(&visitor);
   visitor.SimulateInFramer(kInput, sizeof(kInput));
 
@@ -3330,8 +3291,8 @@
       0xde, 0xad, 0xbe, 0xef,  // Truncated Frame Header
   };
 
-  SpdyFramer framer;
-  TestSpdyVisitor visitor;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   framer.set_visitor(&visitor);
   visitor.SimulateInFramer(kInput, sizeof(kInput));
 
@@ -3365,8 +3326,8 @@
       0x3d, 0x62, 0x61, 0x72,  //
   };
 
-  SpdyFramer framer;
-  TestSpdyVisitor visitor;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   framer.set_visitor(&visitor);
   visitor.SimulateInFramer(kInput, sizeof(kInput));
 
@@ -3380,17 +3341,16 @@
 }
 
 TEST_P(SpdyFramerTest, ReadGarbage) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   unsigned char garbage_frame[256];
   memset(garbage_frame, ~0, sizeof(garbage_frame));
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(garbage_frame, sizeof(garbage_frame));
   EXPECT_EQ(1, visitor.error_count_);
 }
 
 TEST_P(SpdyFramerTest, ReadUnknownExtensionFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
 
   // The unrecognized frame type should still have a valid length.
   const unsigned char unknown_frame[] = {
@@ -3401,11 +3361,10 @@
       0xff, 0xff, 0xff, 0xff,  // Payload
       0xff, 0xff, 0xff, 0xff,  //
   };
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
 
   // Simulate the case where the stream id validation checks out.
   visitor.on_unknown_frame_result_ = true;
-  visitor.use_compression_ = false;
   visitor.SimulateInFramer(unknown_frame, arraysize(unknown_frame));
   EXPECT_EQ(0, visitor.error_count_);
 
@@ -3426,7 +3385,7 @@
 }
 
 TEST_P(SpdyFramerTest, ReadGarbageWithValidLength) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   const unsigned char kFrameData[] = {
       0x00, 0x00, 0x08,        // Length: 8
       0xff,                    //   Type: UnknownFrameType(255)
@@ -3435,8 +3394,7 @@
       0xff, 0xff, 0xff, 0xff,  // Payload
       0xff, 0xff, 0xff, 0xff,  //
   };
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kFrameData, arraysize(kFrameData));
   EXPECT_EQ(1, visitor.error_count_);
 }
@@ -3455,13 +3413,13 @@
       0xff,                    //
   };
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kInput, arraysize(kInput));
   EXPECT_EQ(1, visitor.error_count_);
 }
 
 TEST_P(SpdyFramerTest, SizesTest) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   EXPECT_EQ(9u, framer.GetDataFrameMinimumSize());
   EXPECT_EQ(9u, framer.GetFrameHeaderSize());
   EXPECT_EQ(13u, framer.GetRstStreamMinimumSize());
@@ -3586,7 +3544,7 @@
                                     << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
 
     SpdyDataIR data_ir(1, "hello");
@@ -3635,7 +3593,7 @@
                                     << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
 
     SpdyRstStreamIR rst_stream(13, RST_STREAM_CANCEL);
@@ -3659,7 +3617,7 @@
                                     << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
 
     SpdySettingsIR settings_ir;
@@ -3697,7 +3655,7 @@
                                     << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
 
     SpdyGoAwayIR goaway_ir(97, GOAWAY_OK, "test");
@@ -3720,7 +3678,7 @@
                                     << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
 
     SpdyHeadersIR headers_ir(57);
@@ -3777,7 +3735,7 @@
                                     << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
 
     SpdySerializedFrame frame(framer.SerializePing(SpdyPingIR(42)));
@@ -3803,7 +3761,7 @@
                                     << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
 
     SpdySerializedFrame frame(
@@ -3829,7 +3787,7 @@
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
     testing::StrictMock<test::MockDebugVisitor> debug_visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
     framer.set_debug_visitor(&debug_visitor);
 
@@ -3867,7 +3825,7 @@
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
     testing::StrictMock<test::MockDebugVisitor> debug_visitor;
-    SpdyFramer framer;
+    SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
     framer.set_visitor(&visitor);
     framer.set_debug_visitor(&debug_visitor);
 
@@ -3935,7 +3893,7 @@
   };
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_NO_ERROR));
@@ -3957,7 +3915,7 @@
 
 // Test handling of GOAWAY frames with out-of-bounds status code.
 TEST_P(SpdyFramerTest, GoAwayStatusBounds) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x0a,        // Length: 10
       0x07,                    //   Type: GOAWAY
@@ -3990,7 +3948,7 @@
   };
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   EXPECT_CALL(visitor, OnGoAway(0x7fffffff, GOAWAY_OK));
@@ -4005,7 +3963,7 @@
   const SpdyStreamId kStreamId = 0;
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   EXPECT_CALL(visitor, OnBlocked(kStreamId));
@@ -4023,7 +3981,7 @@
   const SpdyStreamId kStreamId = 1;
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyAltSvcWireFormat::AlternativeService altsvc1(
@@ -4052,7 +4010,7 @@
   const SpdyStreamId kStreamId = 1;
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyAltSvcWireFormat::AlternativeService altsvc1(
@@ -4077,7 +4035,7 @@
 
 TEST_P(SpdyFramerTest, OnAltSvcEmptyProtocolId) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   EXPECT_CALL(visitor, OnError(testing::Eq(&framer)));
@@ -4100,7 +4058,7 @@
   const SpdyStreamId kStreamId = 1;
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_visitor(&visitor);
 
   SpdyAltSvcWireFormat::AlternativeService altsvc(
@@ -4122,7 +4080,7 @@
 
 // Tests handling of ALTSVC frames delivered in small chunks.
 TEST_P(SpdyFramerTest, ReadChunkedAltSvcFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyAltSvcIR altsvc_ir(1);
   SpdyAltSvcWireFormat::AlternativeService altsvc1(
       "pid1", "host", 443, 5, SpdyAltSvcWireFormat::VersionVector());
@@ -4132,8 +4090,7 @@
   altsvc_ir.add_altsvc(altsvc2);
 
   SpdySerializedFrame control_frame(framer.SerializeAltSvc(altsvc_ir));
-  TestSpdyVisitor visitor;
-  visitor.use_compression_ = false;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
 
   // Read data in small chunks.
   size_t framed_data = 0;
@@ -4156,7 +4113,7 @@
 
 // Tests handling of PRIORITY frames.
 TEST_P(SpdyFramerTest, ReadPriority) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyPriorityIR priority(3, 1, 256, false);
   SpdySerializedFrame frame(framer.SerializePriority(priority));
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
@@ -4182,7 +4139,7 @@
       0x00, 0x00, 0x00, 0x01,  // Priority (Truncated)
   };
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
 
   EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state());
@@ -4202,7 +4159,7 @@
       0x00, 0x00, 0x00, 0x01,  // Ping (Truncated)
   };
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
 
   EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state());
@@ -4222,7 +4179,7 @@
       0x00, 0x00, 0x01,        // WindowUpdate (Truncated)
   };
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
 
   EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state());
@@ -4242,7 +4199,7 @@
       0x00, 0x00, 0x01,        // RstStream (Truncated)
   };
 
-  TestSpdyVisitor visitor;
+  TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION);
   visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
 
   EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state());
@@ -4254,8 +4211,9 @@
 // Test that SpdyFramer processes, by default, all passed input in one call
 // to ProcessInput (i.e. will not be calling set_process_single_input_frame()).
 TEST_P(SpdyFramerTest, ProcessAllInput) {
-  SpdyFramer framer;
-  std::unique_ptr<TestSpdyVisitor> visitor(new TestSpdyVisitor);
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+  std::unique_ptr<TestSpdyVisitor> visitor(
+      new TestSpdyVisitor(SpdyFramer::DISABLE_COMPRESSION));
   framer.set_visitor(visitor.get());
 
   // Create two input frames.
@@ -4303,7 +4261,7 @@
 // only processes the first when we give it the first frame split at any point,
 // or give it more than one frame in the input buffer.
 TEST_P(SpdyFramerTest, ProcessAtMostOneFrame) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   framer.set_process_single_input_frame(true);
   std::unique_ptr<TestSpdyVisitor> visitor;
 
@@ -4341,7 +4299,7 @@
 
   for (size_t first_size = 0; first_size <= buf_size; ++first_size) {
     VLOG(1) << "first_size = " << first_size;
-    visitor.reset(new TestSpdyVisitor);
+    visitor.reset(new TestSpdyVisitor(SpdyFramer::DISABLE_COMPRESSION));
     framer.set_visitor(visitor.get());
 
     EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state());
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 21d2df1..913287c 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -678,7 +678,11 @@
   pool_->enable_sending_initial_data_ = enabled;
 }
 
-SpdyTestUtil::SpdyTestUtil() : default_url_(GURL(kDefaultUrl)) {}
+SpdyTestUtil::SpdyTestUtil()
+    : headerless_spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
+      request_spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
+      response_spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
+      default_url_(GURL(kDefaultUrl)) {}
 
 SpdyTestUtil::~SpdyTestUtil() {}
 
@@ -1044,7 +1048,7 @@
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyDataFrame(int stream_id,
                                                          bool fin) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyDataIR data_ir(stream_id,
                      base::StringPiece(kUploadData, kUploadDataSize));
   data_ir.set_fin(fin);
@@ -1055,7 +1059,7 @@
                                                          const char* data,
                                                          uint32_t len,
                                                          bool fin) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
   data_ir.set_fin(fin);
   return SpdySerializedFrame(framer.SerializeData(data_ir));
@@ -1066,7 +1070,7 @@
                                                          uint32_t len,
                                                          bool fin,
                                                          int padding_length) {
-  SpdyFramer framer;
+  SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
   data_ir.set_fin(fin);
   data_ir.set_padding_len(padding_length);
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 9bb2bc67..9a83f46 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -1771,7 +1771,7 @@
   ExpectFlowControlsSynced(
       QuicSessionPeer::GetCryptoStream(client_session)->flow_controller(),
       QuicSessionPeer::GetCryptoStream(server_session)->flow_controller());
-  SpdyFramer spdy_framer;
+  SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION);
   SpdySettingsIR settings_frame;
   settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, false, false,
                             kDefaultMaxUncompressedHeaderSize);
diff --git a/remoting/BUILD.gn b/remoting/BUILD.gn
index 13407e0..1ae1485b 100644
--- a/remoting/BUILD.gn
+++ b/remoting/BUILD.gn
@@ -118,7 +118,7 @@
     "//testing/gtest",
   ]
 
-  if (enable_configuration_policy) {
+  if (!is_ios) {
     deps += [ "//components/policy/core/browser:test_support" ]
   }
 
@@ -177,10 +177,6 @@
       "wtsapi32.lib",
     ]
   }
-
-  if (enable_configuration_policy) {
-    #deps += [ "//components/policy/core/browser:test_support" ]
-  }
 }
 
 if (enable_remoting_host) {
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 481a9594..d066858 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -325,7 +325,7 @@
 
   public_deps = []
 
-  if (enable_configuration_policy) {
+  if (!is_ios) {
     deps += [ "//components/policy:generated" ]
   }
 
@@ -567,7 +567,7 @@
     deps += [ "//remoting/host/win:unit_tests" ]
   }
 
-  if (enable_configuration_policy) {
+  if (!is_ios) {
     deps += [ "//components/policy/core/browser:test_support" ]
   }
 }
@@ -737,7 +737,7 @@
       "//third_party/webrtc/modules/desktop_capture",
     ]
 
-    if (enable_configuration_policy) {
+    if (!is_ios) {
       deps += [ "//components/policy:generated" ]
     }
 
diff --git a/remoting/host/win/BUILD.gn b/remoting/host/win/BUILD.gn
index cb204bf..b034915 100644
--- a/remoting/host/win/BUILD.gn
+++ b/remoting/host/win/BUILD.gn
@@ -132,7 +132,7 @@
     "//ui/events/platform",
   ]
 
-  if (enable_configuration_policy) {
+  if (!is_ios) {
     deps += [ "//components/policy:generated" ]
   }
 
diff --git a/remoting/protocol/webrtc_connection_to_host.cc b/remoting/protocol/webrtc_connection_to_host.cc
index 1e0c788..27b662b2 100644
--- a/remoting/protocol/webrtc_connection_to_host.cc
+++ b/remoting/protocol/webrtc_connection_to_host.cc
@@ -37,7 +37,8 @@
   transport_.reset(new WebrtcTransport(
       jingle_glue::JingleThreadWrapper::current(), transport_context, this));
 
-  transport_->audio_module()->SetAudioTaskRunner(audio_decode_task_runner_);
+  if (audio_decode_task_runner_)
+    transport_->audio_module()->SetAudioTaskRunner(audio_decode_task_runner_);
 
   session_ = std::move(session);
   session_->SetEventHandler(this);
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.cc b/remoting/protocol/webrtc_frame_scheduler_simple.cc
index b84f9db..5b90725 100644
--- a/remoting/protocol/webrtc_frame_scheduler_simple.cc
+++ b/remoting/protocol/webrtc_frame_scheduler_simple.cc
@@ -45,6 +45,21 @@
 // encoded "big" frame may be too large to be delivered to the client quickly.
 const int kEstimatedBytesPerMegapixel = 100000;
 
+// Interval over which the bandwidth estimates is averaged to set target encoder
+// bitrate.
+constexpr base::TimeDelta kBandwidthAveragingInterval =
+    base::TimeDelta::FromSeconds(1);
+
+// Only update encoder bitrate when bandwidth changes by more than 33%. This
+// value is chosen such that the codec is notified about significant changes in
+// bandwidth, while ignoring bandwidth estimate noise. This is necessary because
+// the encoder drops quality every time it's being reconfigured. When using VP8
+// encoder in realtime mode encoded frame size correlates very poorly with the
+// target bitrate, so it's not necessary to set target bitrate to match
+// bandwidth exactly. Send bitrate is controlled more precisely by adjusting
+// time intervals between frames (i.e. FPS).
+const int kEncoderBitrateChangePercentage = 33;
+
 int64_t GetRegionArea(const webrtc::DesktopRegion& region) {
   int64_t result = 0;
   for (webrtc::DesktopRegion::Iterator r(region); !r.IsAtEnd(); r.Advance()) {
@@ -55,11 +70,52 @@
 
 }  // namespace
 
+WebrtcFrameSchedulerSimple::EncoderBitrateFilter::EncoderBitrateFilter() {}
+WebrtcFrameSchedulerSimple::EncoderBitrateFilter::~EncoderBitrateFilter() {}
+
+void WebrtcFrameSchedulerSimple::EncoderBitrateFilter::SetBandwidthEstimate(
+    int bandwidth_kbps,
+    base::TimeTicks now) {
+  while (!bandwidth_samples_.empty() &&
+         now - bandwidth_samples_.front().first > kBandwidthAveragingInterval) {
+    bandwidth_samples_sum_ -= bandwidth_samples_.front().second;
+    bandwidth_samples_.pop();
+  }
+
+  bandwidth_samples_.push(std::make_pair(now, bandwidth_kbps));
+  bandwidth_samples_sum_ += bandwidth_kbps;
+}
+
+int WebrtcFrameSchedulerSimple::EncoderBitrateFilter::GetTargetBitrateKbps(
+    webrtc::DesktopSize size,
+    base::TimeTicks now) {
+  DCHECK(!bandwidth_samples_.empty());
+
+  // TODO(sergeyu): This logic is applicable only to VP8. Reconsider it for
+  // VP9.
+  int bandwidth_estimate = bandwidth_samples_sum_ / bandwidth_samples_.size();
+  int minimum_bitrate =
+      static_cast<int64_t>(kVp8MinimumTargetBitrateKbpsPerMegapixel) *
+      size.width() * size.height() / 1000000LL;
+  int target_bitrate = std::max(minimum_bitrate, bandwidth_estimate);
+
+  // Update encoder bitrate only when it changes by more than 30%. This is
+  // necessary because the encoder resets internal state when it's reconfigured
+  // and this causes visible drop in quality.
+  if (current_target_bitrate_ == 0 ||
+      std::abs(target_bitrate - current_target_bitrate_) >
+          current_target_bitrate_ * kEncoderBitrateChangePercentage / 100) {
+    current_target_bitrate_ = target_bitrate;
+  }
+  return current_target_bitrate_;
+}
+
 WebrtcFrameSchedulerSimple::WebrtcFrameSchedulerSimple()
     : pacing_bucket_(LeakyBucket::kUnlimitedDepth, 0),
       frame_processing_delay_us_(kStatsWindow),
       updated_region_area_(kStatsWindow),
       weak_factory_(this) {}
+
 WebrtcFrameSchedulerSimple::~WebrtcFrameSchedulerSimple() {}
 
 void WebrtcFrameSchedulerSimple::OnKeyFrameRequested() {
@@ -75,10 +131,11 @@
   rtt_estimate_ = rtt;
 }
 
-void WebrtcFrameSchedulerSimple::OnTargetBitrateChanged(int bitrate_kbps) {
+void WebrtcFrameSchedulerSimple::OnTargetBitrateChanged(int bandwidth_kbps) {
   DCHECK(thread_checker_.CalledOnValidThread());
   base::TimeTicks now = base::TimeTicks::Now();
-  pacing_bucket_.UpdateRate(bitrate_kbps * 1000 / 8, now);
+  pacing_bucket_.UpdateRate(bandwidth_kbps * 1000 / 8, now);
+  encoder_bitrate_.SetBandwidthEstimate(bandwidth_kbps, now);
   ScheduleNextFrame(now);
 }
 
@@ -116,12 +173,8 @@
     return false;
   }
 
-  // TODO(sergeyu): This logic is applicable only to VP8. Reconsider it for VP9.
-  int minimum_bitrate =
-      static_cast<int64_t>(kVp8MinimumTargetBitrateKbpsPerMegapixel) *
-      frame.size().width() * frame.size().height() / 1000000LL;
   params_out->bitrate_kbps =
-      std::max(minimum_bitrate, pacing_bucket_.rate() * 8 / 1000);
+      encoder_bitrate_.GetTargetBitrateKbps(frame.size(), now);
 
   params_out->duration = kTargetFrameInterval;
   params_out->key_frame = key_frame_request_;
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.h b/remoting/protocol/webrtc_frame_scheduler_simple.h
index 12855d1c..413dabd8 100644
--- a/remoting/protocol/webrtc_frame_scheduler_simple.h
+++ b/remoting/protocol/webrtc_frame_scheduler_simple.h
@@ -7,6 +7,8 @@
 
 #include "remoting/protocol/webrtc_frame_scheduler.h"
 
+#include <queue>
+
 #include "base/threading/thread_checker.h"
 #include "base/timer/timer.h"
 #include "remoting/base/leaky_bucket.h"
@@ -43,6 +45,22 @@
                       HostFrameStats* frame_stats) override;
 
  private:
+  // Helper class used to calculate target encoder bitrate.
+  class EncoderBitrateFilter {
+   public:
+    EncoderBitrateFilter();
+    ~EncoderBitrateFilter();
+
+    void SetBandwidthEstimate(int bandwidth_kbps, base::TimeTicks now);
+    int GetTargetBitrateKbps(webrtc::DesktopSize size, base::TimeTicks now);
+
+   private:
+    std::queue<std::pair<base::TimeTicks, int>> bandwidth_samples_;
+    int bandwidth_samples_sum_ = 0;
+
+    int current_target_bitrate_;
+  };
+
   void ScheduleNextFrame(base::TimeTicks now);
   void CaptureNextFrame();
 
@@ -54,6 +72,8 @@
 
   LeakyBucket pacing_bucket_;
 
+  EncoderBitrateFilter encoder_bitrate_;
+
   // Set to true when a frame is being captured or encoded.
   bool frame_pending_ = false;
 
diff --git a/services/catalog/reader.cc b/services/catalog/reader.cc
index 766aaf48..ea93418 100644
--- a/services/catalog/reader.cc
+++ b/services/catalog/reader.cc
@@ -34,7 +34,7 @@
 
 base::FilePath GetExecutablePath(const base::FilePath& package_dir,
                                  const std::string& name) {
-  return package_dir.AppendASCII(name + "/" + name + ".service");
+  return package_dir.AppendASCII(name + "/" + name + ".library");
 }
 
 std::unique_ptr<Entry> ProcessManifest(
diff --git a/services/service_manager/background/background_service_manager_main.cc b/services/service_manager/background/background_service_manager_main.cc
index 6902ee4..4ef836f 100644
--- a/services/service_manager/background/background_service_manager_main.cc
+++ b/services/service_manager/background/background_service_manager_main.cc
@@ -9,8 +9,33 @@
 #include "base/debug/debugger.h"
 #include "base/process/launch.h"
 #include "services/service_manager/runner/common/switches.h"
+#include "services/service_manager/runner/host/child_process.h"
 #include "services/service_manager/runner/init.h"
 
+namespace service_manager {
+namespace {
+
+int RunChildProcess() {
+  base::AtExitManager at_exit;
+  InitializeLogging();
+  WaitForDebuggerIfNecessary();
+#if !defined(OFFICIAL_BUILD) && defined(OS_WIN)
+  base::RouteStdioToConsole(false);
+#endif
+  return ChildProcessMain();
+}
+
+}  // namespace
+}  // namespace service_manager
+
 int main(int argc, char** argv) {
+  base::CommandLine::Init(argc, argv);
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kChildProcess)) {
+    return service_manager::RunChildProcess();
+  }
+  // Reset CommandLine as most likely main() is going to use CommandLine too
+  // and expect to be able to initialize it.
+  base::CommandLine::Reset();
   return MasterProcessMain(argc, argv);
 }
diff --git a/services/service_manager/native_runner.h b/services/service_manager/native_runner.h
index 505ed50..9d97c3c 100644
--- a/services/service_manager/native_runner.h
+++ b/services/service_manager/native_runner.h
@@ -25,21 +25,22 @@
  public:
   virtual ~NativeRunner() {}
 
-  // Loads the app in the file at |service_path| and runs it on some other
+  // Loads the app in the file at |app_path| and runs it on some other
   // thread/process. Returns a Service handle the service manager can use to
   // connect to the the app.
   virtual mojom::ServicePtr Start(
+      const base::FilePath& app_path,
       const Identity& target,
       bool start_sandboxed,
       const base::Callback<void(base::ProcessId)>& pid_available_callback,
-      const base::Closure& service_completed_callback) = 0;
+      const base::Closure& app_completed_callback) = 0;
 };
 
 class NativeRunnerFactory {
  public:
   virtual ~NativeRunnerFactory() {}
   virtual std::unique_ptr<NativeRunner> Create(
-      const base::FilePath& service_path) = 0;
+      const base::FilePath& app_path) = 0;
 };
 
 }  // namespace service_manager
diff --git a/services/service_manager/public/cpp/lib/service_runner.cc b/services/service_manager/public/cpp/lib/service_runner.cc
index ac333d2..2b8b388 100644
--- a/services/service_manager/public/cpp/lib/service_runner.cc
+++ b/services/service_manager/public/cpp/lib/service_runner.cc
@@ -71,7 +71,12 @@
 }
 
 MojoResult ServiceRunner::Run(MojoHandle service_request_handle) {
-  return Run(service_request_handle, false);
+  bool init_base = true;
+  if (base::CommandLine::InitializedForCurrentProcess()) {
+    init_base =
+        !base::CommandLine::ForCurrentProcess()->HasSwitch("single-process");
+  }
+  return Run(service_request_handle, init_base);
 }
 
 void ServiceRunner::Quit() {
diff --git a/services/service_manager/public/cpp/service.gni b/services/service_manager/public/cpp/service.gni
index 2fb9991..5faeb25 100644
--- a/services/service_manager/public/cpp/service.gni
+++ b/services/service_manager/public/cpp/service.gni
@@ -11,13 +11,12 @@
 }
 
 # Generates a Service "package", which includes:
+# . A self-named subdirectory
+# . A binary .library
+# . A resources subdirectory alongside .library that contains the contents of
+#   "resources"
 #
-#   - A self-named subdirectory
-#   - A binary .service executable
-#   - A resources subdirectory alongside the executable, which contains the
-#     contents of "resources"
-#
-# The parameters of this template are those of an executable
+# The parameters of this template are those of a shared library.
 template("service") {
   base_target_name = target_name
   if (defined(invoker.output_name)) {
@@ -26,13 +25,12 @@
 
   final_target_name = target_name
 
-  service_deps = []
+  library_deps = []
   if (defined(invoker.deps)) {
-    service_deps += invoker.deps
+    library_deps += invoker.deps
   }
 
-  service_data_deps =
-      [ "//services/service_manager/public/cpp/standalone_service:main" ]
+  library_data_deps = []
 
   if (defined(invoker.resources)) {
     copy_step_name = "${base_target_name}__copy_resources"
@@ -44,22 +42,16 @@
       if (defined(invoker.testonly)) {
         testonly = invoker.testonly
       }
-      deps = service_deps
+      deps = library_deps
     }
-    service_data_deps += [ ":$copy_step_name" ]
+    library_data_deps += [ ":$copy_step_name" ]
   }
 
-  if (defined(invoker.data_deps)) {
-    service_data_deps += invoker.data_deps
-  }
+  output = base_target_name + ".library"
+  library_target_name = base_target_name + "_library"
+  library_name = "${shlib_prefix}${library_target_name}${shlib_extension}"
 
-  executable_target_name = base_target_name + "_executable"
-  executable_name = base_target_name + ".service"
-
-  executable(executable_target_name) {
-    output_name = base_target_name
-    output_extension = "service"
-
+  shared_library(library_target_name) {
     if (defined(invoker.cflags)) {
       cflags = invoker.cflags
     }
@@ -91,13 +83,22 @@
       libs = invoker.libs
     }
 
-    data_deps = service_data_deps
+    data_deps = []
+    if (!defined(invoker.avoid_runner_cycle) || !invoker.avoid_runner_cycle) {
+      # Give the user an out; as some Services are depended on by the runner.
+      data_deps += [ "//services/service_manager/standalone" ]
+    }
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
+    }
+    data_deps += library_data_deps
 
     deps = [
-      "//services/service_manager/public/cpp/standalone_service:main",
+      "//mojo/public/c/system:set_thunks_for_app",
+      "//services/service_manager/public/cpp:application_support",
     ]
 
-    deps += service_deps
+    deps += library_deps
     if (defined(invoker.public_deps)) {
       public_deps = invoker.public_deps
     }
@@ -137,20 +138,14 @@
                              "visibility",
                            ])
     deps = [
-      ":${executable_target_name}",
+      ":${library_target_name}",
     ]
 
-    # NOTE: We have to explicitly inherit the same data_deps as the executable
-    # target itself, rather than specifying a data depenedency on the executable
-    # target. This avoids needless duplication of service binary artifacts in
-    # test isolates, as the executable is unused in its original location.
-    data_deps = service_data_deps
-
     sources = [
-      "${root_out_dir}/${executable_name}",
+      "${root_shlib_dir}/${library_name}",
     ]
     outputs = [
-      "${root_out_dir}/${packages_directory}/${base_target_name}/${executable_name}",
+      "${root_out_dir}/${packages_directory}/${base_target_name}/${output}",
     ]
   }
 }
diff --git a/services/service_manager/public/cpp/service.h b/services/service_manager/public/cpp/service.h
index b3322b2..2b8e780d 100644
--- a/services/service_manager/public/cpp/service.h
+++ b/services/service_manager/public/cpp/service.h
@@ -5,8 +5,6 @@
 #ifndef SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_H_
 #define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_H_
 
-#include "base/macros.h"
-
 namespace service_manager {
 
 class InterfaceRegistry;
@@ -67,8 +65,6 @@
   void set_context(ServiceContext* context) { service_context_ = context; }
 
   ServiceContext* service_context_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(Service);
 };
 
 // TODO(rockot): Remove this. It's here to satisfy a few remaining use cases
@@ -79,16 +75,14 @@
   explicit ForwardingService(Service* target);
   ~ForwardingService() override;
 
+ private:
   // Service:
   void OnStart() override;
   bool OnConnect(const ServiceInfo& remote_info,
                  InterfaceRegistry* registry) override;
   bool OnStop() override;
 
- private:
   Service* const target_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(ForwardingService);
 };
 
 }  // namespace service_manager
diff --git a/services/service_manager/public/cpp/standalone_service/BUILD.gn b/services/service_manager/public/cpp/standalone_service/BUILD.gn
deleted file mode 100644
index 433ddf68..0000000
--- a/services/service_manager/public/cpp/standalone_service/BUILD.gn
+++ /dev/null
@@ -1,63 +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.
-
-source_set("standalone_service") {
-  sources = [
-    "standalone_service.cc",
-    "standalone_service.h",
-    "switches.cc",
-    "switches.h",
-  ]
-
-  deps = [
-    "//mojo/edk/system",
-    "//mojo/public/cpp/system",
-    "//services/service_manager/public/cpp",
-    "//services/service_manager/runner:init",
-    "//services/service_manager/runner/common",
-  ]
-
-  public_deps = [
-    "//base",
-    "//services/service_manager/public/interfaces",
-  ]
-
-  if (is_linux && !is_android) {
-    sources += [
-      "linux_sandbox.cc",
-      "linux_sandbox.h",
-    ]
-
-    deps += [
-      "//sandbox/linux:sandbox",
-      "//sandbox/linux:sandbox_services",
-      "//sandbox/linux:seccomp_bpf",
-    ]
-  }
-
-  if (is_mac) {
-    sources += [
-      "mach_broker.cc",
-      "mach_broker.h",
-    ]
-  }
-}
-
-# Service executable targets should link against this to get a boilerplate entry
-# point which accepts canonical command-line arguments to establish a connection
-# to the Service Manager. In order to link properly, dependents must ensure that
-# they define a ServiceMain() symbol which matches the signature in
-# //services/service_manager/public/c/main.h.
-source_set("main") {
-  sources = [
-    "main.cc",
-  ]
-
-  deps = [
-    ":standalone_service",
-    "//base",
-    "//base:i18n",
-    "//services/service_manager/runner:init",
-  ]
-}
diff --git a/services/service_manager/public/cpp/standalone_service/DEPS b/services/service_manager/public/cpp/standalone_service/DEPS
deleted file mode 100644
index ec69c8f..0000000
--- a/services/service_manager/public/cpp/standalone_service/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+sandbox",
-]
diff --git a/services/service_manager/public/cpp/standalone_service/main.cc b/services/service_manager/public/cpp/standalone_service/main.cc
deleted file mode 100644
index a505f8b..0000000
--- a/services/service_manager/public/cpp/standalone_service/main.cc
+++ /dev/null
@@ -1,95 +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 "base/at_exit.h"
-#include "base/base_paths.h"
-#include "base/command_line.h"
-#include "base/debug/stack_trace.h"
-#include "base/i18n/icu_util.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/path_service.h"
-#include "base/process/launch.h"
-#include "services/service_manager/public/c/main.h"
-#include "services/service_manager/public/cpp/standalone_service/standalone_service.h"
-#include "services/service_manager/public/cpp/standalone_service/switches.h"
-#include "services/service_manager/public/interfaces/service.mojom.h"
-#include "services/service_manager/runner/init.h"
-
-#if defined(OS_MACOSX)
-#include "base/mac/bundle_locations.h"
-#endif
-
-namespace {
-
-// Cross-platform helper to override the necessary hard-coded path location
-// used to load ICU data.
-//
-// TODO(rockot): We should just parameterize InitializeICU so hacks like
-// this are unnecessary.
-class ScopedIcuDataDirOverride {
- public:
-  ScopedIcuDataDirOverride(const base::FilePath& path) {
-#if defined(OS_WIN)
-    DCHECK(base::PathService::Get(base::DIR_MODULE, &original_path_));
-#elif defined(OS_MACOSX)
-    original_path_= base::mac::FrameworkBundlePath();
-#else
-    DCHECK(base::PathService::Get(base::DIR_EXE, &original_path_));
-#endif
-
-    if (!path.empty())
-      SetPathOverride(path);
-  }
-
-  ~ScopedIcuDataDirOverride() { SetPathOverride(original_path_); }
-
- private:
-  static void SetPathOverride(const base::FilePath& path) {
-#if defined(OS_WIN)
-    DCHECK(base::PathService::Override(base::DIR_MODULE, path));
-#elif defined(OS_MACOSX)
-    base::mac::SetOverrideFrameworkBundlePath(path);
-#else
-    DCHECK(base::PathService::Override(base::DIR_EXE, path));
-#endif
-  }
-
-  base::FilePath original_path_;
-};
-
-// TODO(rockot): We should consider removing ServiceMain and instead allowing
-// service sources to define a CreateService method which returns a new instance
-// of service_manager::Service. This would reduce boilerplate in service sources
-// since they all effectively do the same thing via
-// service_manager::ServiceRunner.
-void RunServiceMain(service_manager::mojom::ServiceRequest request) {
-  MojoResult result = ServiceMain(request.PassMessagePipe().release().value());
-  DCHECK_EQ(result, MOJO_RESULT_OK);
-}
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::AtExitManager at_exit;
-  base::CommandLine::Init(argc, argv);
-
-#if !defined(OFFICIAL_BIULD) && defined(OS_WIN)
-  base::RouteStdioToConsole(false);
-#endif
-
-  service_manager::InitializeLogging();
-
-  {
-    base::FilePath icu_data_dir = base::CommandLine::ForCurrentProcess()
-        ->GetSwitchValuePath(service_manager::switches::kIcuDataDir);
-    ScopedIcuDataDirOverride path_override(icu_data_dir);
-    base::i18n::InitializeICU();
-  }
-
-  service_manager::WaitForDebuggerIfNecessary();
-
-  service_manager::RunStandaloneService(base::Bind(&RunServiceMain));
-  return 0;
-}
diff --git a/services/service_manager/public/cpp/standalone_service/standalone_service.h b/services/service_manager/public/cpp/standalone_service/standalone_service.h
deleted file mode 100644
index e7ea3a7..0000000
--- a/services/service_manager/public/cpp/standalone_service/standalone_service.h
+++ /dev/null
@@ -1,25 +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 "base/callback.h"
-#include "services/service_manager/public/interfaces/service.mojom.h"
-
-namespace service_manager {
-
-using StandaloneServiceCallback = base::Callback<void(mojom::ServiceRequest)>;
-
-// Runs a standalone service in the current process. This takes care of setting
-// up a boilerplate environment, including initializing //base objects, Mojo
-// IPC, running a MessageLoop, and establishing a connection to the Service
-// Manager via canonical command-line arguments.
-//
-// Once a Service request is obtained, |callback| is invoked with it. This call
-// blocks until |callback| returns.
-//
-// NOTE: A typical service should also link against the main() defined in
-// main.cc (next to this header) and thus have no need to call this function
-// directly.
-void RunStandaloneService(const StandaloneServiceCallback& callback);
-
-}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/standalone_service/switches.cc b/services/service_manager/public/cpp/standalone_service/switches.cc
deleted file mode 100644
index 58e07a0..0000000
--- a/services/service_manager/public/cpp/standalone_service/switches.cc
+++ /dev/null
@@ -1,15 +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 "services/service_manager/public/cpp/standalone_service/switches.h"
-
-namespace service_manager {
-namespace switches {
-
-// The path where ICU initialization data can be found. If not provided, the
-// service binary's directory is assumed.
-const char kIcuDataDir[] = "icu-data-dir";
-
-}  // namespace switches
-}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/standalone_service/switches.h b/services/service_manager/public/cpp/standalone_service/switches.h
deleted file mode 100644
index b6f8508..0000000
--- a/services/service_manager/public/cpp/standalone_service/switches.h
+++ /dev/null
@@ -1,11 +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.
-
-namespace service_manager {
-namespace switches {
-
-extern const char kIcuDataDir[];
-
-}  // namespace switches
-}  // namespace service_manager
diff --git a/services/service_manager/runner/BUILD.gn b/services/service_manager/runner/BUILD.gn
index 0bf0e223c..83317470 100644
--- a/services/service_manager/runner/BUILD.gn
+++ b/services/service_manager/runner/BUILD.gn
@@ -5,6 +5,7 @@
 group("runner") {
   testonly = true
   deps = [
+    "//services/service_manager/runner/child",
     "//services/service_manager/runner/host",
   ]
 }
diff --git a/services/service_manager/runner/child/BUILD.gn b/services/service_manager/runner/child/BUILD.gn
new file mode 100644
index 0000000..fc73aa6
--- /dev/null
+++ b/services/service_manager/runner/child/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+group("child") {
+  testonly = true
+  deps = [
+    ":test_native_main",
+  ]
+}
+
+source_set("test_native_main") {
+  sources = [
+    "test_native_main.cc",
+    "test_native_main.h",
+  ]
+
+  public_deps = [
+    "//services/service_manager/runner:init",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/edk/system",
+    "//services/service_manager/public/cpp",
+    "//services/service_manager/runner/common",
+  ]
+}
diff --git a/services/service_manager/runner/child/manifest.json b/services/service_manager/runner/child/manifest.json
new file mode 100644
index 0000000..b49ff07
--- /dev/null
+++ b/services/service_manager/runner/child/manifest.json
@@ -0,0 +1,11 @@
+{
+  "name": "mojo_runner_child_apptest",
+  "display_name": "Runner Child Apptest",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "requires": {
+        "*": [ "app" ]
+      }
+    }
+  }
+}
diff --git a/services/service_manager/runner/child/test_native_main.cc b/services/service_manager/runner/child/test_native_main.cc
new file mode 100644
index 0000000..75806cc
--- /dev/null
+++ b/services/service_manager/runner/child/test_native_main.cc
@@ -0,0 +1,71 @@
+// Copyright 2015 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 "services/service_manager/runner/child/test_native_main.h"
+
+#include <utility>
+
+#include "base/debug/stack_trace.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/launch.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/process_delegate.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_context.h"
+#include "services/service_manager/runner/common/client_util.h"
+#include "services/service_manager/runner/init.h"
+
+namespace service_manager {
+namespace {
+
+class ProcessDelegate : public mojo::edk::ProcessDelegate {
+ public:
+  ProcessDelegate() {}
+  ~ProcessDelegate() override {}
+
+ private:
+  void OnShutdownComplete() override {}
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessDelegate);
+};
+
+}  // namespace
+
+int TestNativeMain(std::unique_ptr<service_manager::Service> service) {
+  service_manager::WaitForDebuggerIfNecessary();
+
+#if !defined(OFFICIAL_BUILD)
+  base::debug::EnableInProcessStackDumping();
+#if defined(OS_WIN)
+  base::RouteStdioToConsole(false);
+#endif
+#endif
+
+  {
+    mojo::edk::Init();
+
+    ProcessDelegate process_delegate;
+    base::Thread io_thread("io_thread");
+    base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
+    CHECK(io_thread.StartWithOptions(io_thread_options));
+
+    mojo::edk::InitIPCSupport(&process_delegate, io_thread.task_runner());
+    mojo::edk::SetParentPipeHandleFromCommandLine();
+
+    base::MessageLoop loop;
+    service_manager::ServiceContext context(
+        std::move(service),
+        service_manager::GetServiceRequestFromCommandLine());
+    base::RunLoop().Run();
+    mojo::edk::ShutdownIPCSupport();
+  }
+
+  return 0;
+}
+
+}  // namespace service_manager
diff --git a/services/service_manager/runner/child/test_native_main.h b/services/service_manager/runner/child/test_native_main.h
new file mode 100644
index 0000000..7148a96
--- /dev/null
+++ b/services/service_manager/runner/child/test_native_main.h
@@ -0,0 +1,18 @@
+// Copyright 2015 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 SERVICES_SERVICE_MANAGER_RUNNER_CHILD_TEST_NATIVE_MAIN_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_CHILD_TEST_NATIVE_MAIN_H_
+
+#include <memory>
+
+namespace service_manager {
+
+class Service;
+
+int TestNativeMain(std::unique_ptr<Service> service);
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_CHILD_TEST_NATIVE_MAIN_H_
diff --git a/services/service_manager/runner/host/BUILD.gn b/services/service_manager/runner/host/BUILD.gn
index f798dca8..e110ccb236 100644
--- a/services/service_manager/runner/host/BUILD.gn
+++ b/services/service_manager/runner/host/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//services/service_manager/public/cpp/service.gni")
-import("//services/service_manager/public/service_manifest.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//testing/test.gni")
 
@@ -16,19 +15,81 @@
   ]
 }
 
+source_set("native_library_runner") {
+  sources = [
+    "native_library_runner.cc",
+    "native_library_runner.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/edk/system",
+    "//services/service_manager",
+  ]
+
+  # This target has to include the public thunk headers, which generally
+  # shouldn't be included without picking an implementation. We are providing
+  # the implementation but the thunk header target cannot declare that we are
+  # permitted to include it since it's in the public SDK and we are not.
+  # Suppress include checking so we can still check the rest of the targets in
+  # this file.
+  check_includes = false
+}
+
+source_set("child_process_base") {
+  sources = [
+    "child_process_base.cc",
+    "child_process_base.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/edk/system",
+    "//services/service_manager",
+    "//services/service_manager/public/interfaces",
+    "//services/service_manager/runner:init",
+    "//services/service_manager/runner/common",
+  ]
+
+  if (is_linux && !is_android) {
+    sources += [
+      "linux_sandbox.cc",
+      "linux_sandbox.h",
+    ]
+
+    deps += [
+      "//sandbox/linux:sandbox",
+      "//sandbox/linux:sandbox_services",
+      "//sandbox/linux:seccomp_bpf",
+    ]
+  }
+
+  if (is_mac) {
+    sources += [
+      "mach_broker.cc",
+      "mach_broker.h",
+    ]
+  }
+}
+
 source_set("lib") {
   sources = [
+    "child_process.cc",
+    "child_process.h",
     "child_process_host.cc",
     "child_process_host.h",
+    "in_process_native_runner.cc",
+    "in_process_native_runner.h",
     "out_of_process_native_runner.cc",
     "out_of_process_native_runner.h",
   ]
 
   deps = [
+    ":child_process_base",
+    ":native_library_runner",
     "//base:base_static",
     "//base:i18n",
     "//services/service_manager/public/cpp:sources",
-    "//services/service_manager/public/cpp/standalone_service",
     "//services/service_manager/runner:init",
     "//services/service_manager/runner/common",
   ]
@@ -50,6 +111,7 @@
   sources = [
     "child_process_host_unittest.cc",
     "host_unittests.cc",
+    "in_process_native_runner_unittest.cc",
   ]
 
   deps = [
@@ -62,28 +124,4 @@
     "//services/service_manager/runner/common",
     "//testing/gtest",
   ]
-
-  data_deps = [
-    ":host_test_service",
-  ]
-}
-
-service("host_test_service") {
-  sources = [
-    "host_test_service_main.cc",
-  ]
-
-  deps = [
-    "//mojo/public/cpp/system",
-    "//services/service_manager/public/cpp/standalone_service:main",
-  ]
-
-  data_deps = [
-    ":host_test_service_manifest",
-  ]
-}
-
-service_manifest("host_test_service_manifest") {
-  name = "host_test_service"
-  source = "host_test_service_manifest.json"
 }
diff --git a/services/service_manager/runner/host/child_process.cc b/services/service_manager/runner/host/child_process.cc
new file mode 100644
index 0000000..06a7339
--- /dev/null
+++ b/services/service_manager/runner/host/child_process.cc
@@ -0,0 +1,72 @@
+// Copyright 2014 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 "services/service_manager/runner/host/child_process.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/i18n/icu_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/process_delegate.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/core.h"
+#include "services/service_manager/runner/common/switches.h"
+#include "services/service_manager/runner/host/child_process_base.h"
+#include "services/service_manager/runner/host/native_library_runner.h"
+#include "services/service_manager/runner/init.h"
+
+namespace service_manager {
+
+namespace {
+
+void RunNativeLibrary(base::NativeLibrary library,
+                      mojom::ServiceRequest service_request) {
+  if (!RunServiceInNativeLibrary(library, std::move(service_request))) {
+    LOG(ERROR) << "Failure to RunServiceInNativeLibrary()";
+  }
+}
+
+}  // namespace
+
+int ChildProcessMain() {
+  DVLOG(2) << "ChildProcessMain()";
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  base::NativeLibrary library = 0;
+  // Load the application library before we engage the sandbox.
+  base::FilePath library_path =
+      command_line.GetSwitchValuePath(switches::kChildProcess);
+  if (!library_path.empty())
+    library = LoadNativeLibrary(library_path);
+  base::i18n::InitializeICU();
+  if (library)
+    CallLibraryEarlyInitialization(library);
+
+  ChildProcessMainWithCallback(base::Bind(&RunNativeLibrary, library));
+
+  return 0;
+}
+
+}  // namespace service_manager
diff --git a/services/service_manager/runner/host/child_process.h b/services/service_manager/runner/host/child_process.h
new file mode 100644
index 0000000..3f903d2
--- /dev/null
+++ b/services/service_manager/runner/host/child_process.h
@@ -0,0 +1,15 @@
+// Copyright 2014 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 SERVICES_SERVICE_MANAGER_RUNNER_HOST_CHILD_PROCESS_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_HOST_CHILD_PROCESS_H_
+
+namespace service_manager {
+
+// Main method for a child process.
+int ChildProcessMain();
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_CHILD_PROCESS_H_
diff --git a/services/service_manager/public/cpp/standalone_service/standalone_service.cc b/services/service_manager/runner/host/child_process_base.cc
similarity index 78%
rename from services/service_manager/public/cpp/standalone_service/standalone_service.cc
rename to services/service_manager/runner/host/child_process_base.cc
index 5e42b77..6341ecc 100644
--- a/services/service_manager/public/cpp/standalone_service/standalone_service.cc
+++ b/services/service_manager/runner/host/child_process_base.cc
@@ -1,33 +1,35 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2014 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 "services/service_manager/public/cpp/standalone_service/standalone_service.h"
+#include "services/service_manager/runner/host/child_process_base.h"
 
 #include "base/command_line.h"
 #include "base/debug/stack_trace.h"
 #include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/process_delegate.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "services/service_manager/public/cpp/service_context.h"
 #include "services/service_manager/runner/common/client_util.h"
 #include "services/service_manager/runner/common/switches.h"
 
 #if defined(OS_LINUX)
 #include "base/rand_util.h"
 #include "base/sys_info.h"
-#include "services/service_manager/public/cpp/standalone_service/linux_sandbox.h"
+#include "services/service_manager/runner/host/linux_sandbox.h"
 #endif
 
 #if defined(OS_MACOSX)
-#include "services/service_manager/public/cpp/standalone_service/mach_broker.h"
+#include "services/service_manager/runner/host/mach_broker.h"
 #endif
 
 namespace service_manager {
+
 namespace {
 
 #if defined(OS_LINUX)
@@ -63,10 +65,16 @@
         wait_for_shutdown_event_(
             base::WaitableEvent::ResetPolicy::MANUAL,
             base::WaitableEvent::InitialState::NOT_SIGNALED) {
+    // Initialize Mojo before starting any threads.
     mojo::edk::Init();
-    DCHECK(io_thread_.StartWithOptions(
-              base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
-    mojo::edk::InitIPCSupport(this, io_thread_.task_runner());
+
+    // Create and start our I/O thread.
+    base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
+    CHECK(io_thread_.StartWithOptions(io_thread_options));
+    io_runner_ = io_thread_.task_runner().get();
+    CHECK(io_runner_.get());
+
+    mojo::edk::InitIPCSupport(this, io_runner_);
     mojo::edk::SetParentPipeHandleFromCommandLine();
   }
 
@@ -82,6 +90,7 @@
   }
 
   base::Thread io_thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
 
   // Used to unblock the main thread on shutdown.
   base::WaitableEvent wait_for_shutdown_event_;
@@ -91,7 +100,7 @@
 
 }  // namespace
 
-void RunStandaloneService(const StandaloneServiceCallback& callback) {
+void ChildProcessMainWithCallback(const RunCallback& callback) {
   DCHECK(!base::MessageLoop::current());
 
 #if defined(OS_MACOSX)
diff --git a/services/service_manager/runner/host/child_process_base.h b/services/service_manager/runner/host/child_process_base.h
new file mode 100644
index 0000000..0d85adf
--- /dev/null
+++ b/services/service_manager/runner/host/child_process_base.h
@@ -0,0 +1,23 @@
+// 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 SERVICES_SERVICE_MANAGER_RUNNER_HOST_CHILD_PROCESS_BASE_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_HOST_CHILD_PROCESS_BASE_H_
+
+#include "base/callback.h"
+#include "services/service_manager/public/interfaces/service.mojom.h"
+
+namespace service_manager {
+
+// Child processes call this to establish the connection to the service manager
+// and obtain
+// the ServiceRequest. Once the connection has been established |callback|
+// is run. ChildProcessMainWithCallback() returns once the the callback
+// completes.
+using RunCallback = base::Callback<void(mojom::ServiceRequest)>;
+void ChildProcessMainWithCallback(const RunCallback& callback);
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_CHILD_PROCESS_BASE_H_
diff --git a/services/service_manager/runner/host/child_process_host.cc b/services/service_manager/runner/host/child_process_host.cc
index 067f432..4f1de53 100644
--- a/services/service_manager/runner/host/child_process_host.cc
+++ b/services/service_manager/runner/host/child_process_host.cc
@@ -8,14 +8,12 @@
 
 #include <utility>
 
-#include "base/base_paths.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
-#include "base/path_service.h"
 #include "base/process/kill.h"
 #include "base/process/launch.h"
 #include "base/synchronization/lock.h"
@@ -25,7 +23,6 @@
 #include "mojo/public/cpp/bindings/interface_ptr_info.h"
 #include "mojo/public/cpp/system/core.h"
 #include "services/service_manager/native_runner_delegate.h"
-#include "services/service_manager/public/cpp/standalone_service/switches.h"
 #include "services/service_manager/runner/common/client_util.h"
 #include "services/service_manager/runner/common/switches.h"
 
@@ -38,7 +35,7 @@
 #endif
 
 #if defined(OS_MACOSX)
-#include "services/service_manager/public/cpp/standalone_service/mach_broker.h"
+#include "services/service_manager/runner/host/mach_broker.h"
 #endif
 
 namespace service_manager {
@@ -47,24 +44,23 @@
                                    NativeRunnerDelegate* delegate,
                                    bool start_sandboxed,
                                    const Identity& target,
-                                   const base::FilePath& service_path)
+                                   const base::FilePath& app_path)
     : launch_process_runner_(launch_process_runner),
       delegate_(delegate),
       start_sandboxed_(start_sandboxed),
       target_(target),
-      service_path_(service_path),
+      app_path_(app_path),
       child_token_(mojo::edk::GenerateRandomToken()),
       start_child_process_event_(
           base::WaitableEvent::ResetPolicy::AUTOMATIC,
           base::WaitableEvent::InitialState::NOT_SIGNALED),
-      weak_factory_(this) {
-  if (service_path_.empty())
-    service_path_ = base::CommandLine::ForCurrentProcess()->GetProgram();
-}
+      weak_factory_(this) {}
 
 ChildProcessHost::~ChildProcessHost() {
-  DCHECK(!mojo_ipc_channel_)
-      << "Destroying ChildProcessHost before calling Join";
+  if (!app_path_.empty()) {
+    CHECK(!mojo_ipc_channel_)
+        << "Destroying ChildProcessHost before calling Join";
+  }
 }
 
 mojom::ServicePtr ChildProcessHost::Start(
@@ -73,21 +69,30 @@
     const base::Closure& quit_closure) {
   DCHECK(!child_process_.IsValid());
 
-  const base::CommandLine& parent_command_line =
-      *base::CommandLine::ForCurrentProcess();
+  const base::CommandLine* parent_command_line =
+      base::CommandLine::ForCurrentProcess();
+  base::FilePath target_path = parent_command_line->GetProgram();
+  // |app_path_| can be empty in tests.
+  if (!app_path_.MatchesExtension(FILE_PATH_LITERAL(".library")) &&
+      !app_path_.empty()) {
+    target_path = app_path_;
+  }
 
   std::unique_ptr<base::CommandLine> child_command_line(
-      new base::CommandLine(service_path_));
+      new base::CommandLine(target_path));
 
-  child_command_line->AppendArguments(parent_command_line, false);
+  child_command_line->AppendArguments(*parent_command_line, false);
 
 #ifndef NDEBUG
   child_command_line->AppendSwitchASCII("n", target.name());
   child_command_line->AppendSwitchASCII("u", target.user_id());
 #endif
 
+  if (target_path != app_path_)
+    child_command_line->AppendSwitchPath(switches::kChildProcess, app_path_);
+
   if (start_sandboxed_)
-    child_command_line->AppendSwitch(::switches::kEnableSandbox);
+    child_command_line->AppendSwitch(switches::kEnableSandbox);
 
   mojo_ipc_channel_.reset(new mojo::edk::PlatformChannelPair);
   mojo_ipc_channel_->PrepareToPassClientHandleToChildProcess(
@@ -135,24 +140,6 @@
   }
 
   base::LaunchOptions options;
-
-  base::FilePath exe_dir;
-  DCHECK(base::PathService::Get(base::DIR_EXE, &exe_dir));
-  options.current_directory = exe_dir;
-
-  // The service should look for ICU data next to the service runner's
-  // executable rather than its own.
-  child_command_line->AppendSwitchPath(switches::kIcuDataDir, exe_dir);
-
-#if defined(OS_POSIX)
-  // We need the dynamic loader to be able to locate things like libbase.so
-  // in component builds, as well as some other dynamic runtime dependencies in
-  // other build environments (e.g. libosmesa.so). For this we set
-  // LD_LIBRARY_PATH to the service runner's executable path where such
-  // artifacts are typically expected to reside.
-  options.environ["LD_LIBRARY_PATH"] = exe_dir.value();
-#endif
-
 #if defined(OS_WIN)
   options.handles_to_inherit = &handle_passing_info_;
 #if defined(OFFICIAL_BUILD)
diff --git a/services/service_manager/runner/host/child_process_host.h b/services/service_manager/runner/host/child_process_host.h
index ab87d02..eb14adb 100644
--- a/services/service_manager/runner/host/child_process_host.h
+++ b/services/service_manager/runner/host/child_process_host.h
@@ -36,25 +36,26 @@
 
 // This class represents a "child process host". Handles launching and
 // connecting a platform-specific "pipe" to the child, and supports joining the
-// child process. Currently runs a single service, loaded from a standalone
-// service executable on the file system.
+// child process. Currently runs a single app (loaded from the file system).
 //
 // This class is not thread-safe. It should be created/used/destroyed on a
 // single thread.
 //
 // Note: Does not currently work on Windows before Vista.
+// Note: After |Start()|, |StartApp| must be called and this object must
+// remained alive until the |on_app_complete| callback is called.
 class ChildProcessHost {
  public:
   using ProcessReadyCallback = base::Callback<void(base::ProcessId)>;
 
   // |name| is just for debugging ease. We will spawn off a process so that it
-  // can be sandboxed if |start_sandboxed| is true. |service_path| is a path to
-  // the service executable we wish to start.
+  // can be sandboxed if |start_sandboxed| is true. |app_path| is a path to the
+  // service we wish to start.
   ChildProcessHost(base::TaskRunner* launch_process_runner,
                    NativeRunnerDelegate* delegate,
                    bool start_sandboxed,
                    const Identity& target,
-                   const base::FilePath& service_path);
+                   const base::FilePath& app_path);
   virtual ~ChildProcessHost();
 
   // |Start()|s the child process; calls |DidStart()| (on the thread on which
@@ -76,7 +77,7 @@
   NativeRunnerDelegate* delegate_ = nullptr;
   bool start_sandboxed_ = false;
   Identity target_;
-  base::FilePath service_path_;
+  const base::FilePath app_path_;
   base::Process child_process_;
 
   // Used to initialize the Mojo IPC channel between parent and child.
diff --git a/services/service_manager/runner/host/child_process_host_unittest.cc b/services/service_manager/runner/host/child_process_host_unittest.cc
index a8804929..4ccee538 100644
--- a/services/service_manager/runner/host/child_process_host_unittest.cc
+++ b/services/service_manager/runner/host/child_process_host_unittest.cc
@@ -26,12 +26,6 @@
 namespace service_manager {
 namespace {
 
-const char kTestServiceName[] = "host_test_service";
-
-const base::FilePath::CharType kPackagesPath[] = FILE_PATH_LITERAL("Packages");
-const base::FilePath::CharType kServiceExtension[] =
-    FILE_PATH_LITERAL(".service");
-
 void ProcessReadyCallbackAdapater(const base::Closure& callback,
                                   base::ProcessId process_id) {
   callback.Run();
@@ -77,6 +71,8 @@
 #else
 #define MAYBE_StartJoin StartJoin
 #endif  // defined(OS_ANDROID)
+// Just tests starting the child process and joining it (without starting an
+// app).
 TEST(ChildProcessHostTest, MAYBE_StartJoin) {
   base::FilePath service_manager_dir;
   PathService::Get(base::DIR_MODULE, &service_manager_dir);
@@ -93,14 +89,10 @@
   ProcessDelegate delegate;
   mojo::edk::InitIPCSupport(&delegate, io_thread.task_runner());
 
-  base::FilePath test_service_path =
-      base::FilePath(kPackagesPath).AppendASCII(kTestServiceName)
-          .AppendASCII(kTestServiceName) .AddExtension(kServiceExtension);
-
   NativeRunnerDelegateImpl native_runner_delegate;
   ChildProcessHost child_process_host(blocking_pool.get(),
                                       &native_runner_delegate, false,
-                                      Identity(), test_service_path);
+                                      Identity(), base::FilePath());
   base::RunLoop run_loop;
   child_process_host.Start(
       Identity(),
diff --git a/services/service_manager/runner/host/host_test_service_main.cc b/services/service_manager/runner/host/host_test_service_main.cc
deleted file mode 100644
index 2ce8492f..0000000
--- a/services/service_manager/runner/host/host_test_service_main.cc
+++ /dev/null
@@ -1,12 +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/system/message_pipe.h"
-#include "services/service_manager/public/c/main.h"
-
-MojoResult ServiceMain(MojoHandle service_request_handle) {
-  mojo::ScopedMessagePipeHandle service_request_pipe;
-  service_request_pipe.reset(mojo::MessagePipeHandle(service_request_handle));
-  return MOJO_RESULT_OK;
-}
diff --git a/services/service_manager/runner/host/host_test_service_manifest.json b/services/service_manager/runner/host/host_test_service_manifest.json
deleted file mode 100644
index 1307358..0000000
--- a/services/service_manager/runner/host/host_test_service_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "host_test_service",
-  "display_name": "Service Host Test Service"
-}
diff --git a/services/service_manager/runner/host/host_unittests.cc b/services/service_manager/runner/host/host_unittests.cc
index ed09c1c..2e91efc 100644
--- a/services/service_manager/runner/host/host_unittests.cc
+++ b/services/service_manager/runner/host/host_unittests.cc
@@ -10,14 +10,23 @@
 #include "base/test/test_suite.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "services/service_manager/runner/common/switches.h"
+#include "services/service_manager/runner/host/child_process.h"
 #include "services/service_manager/runner/init.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 int main(int argc, char** argv) {
   base::CommandLine::Init(argc, argv);
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
 
   service_manager::WaitForDebuggerIfNecessary();
 
+  if (command_line.HasSwitch(switches::kChildProcess)) {
+    base::AtExitManager at_exit;
+
+    return service_manager::ChildProcessMain();
+  }
+
   mojo::edk::Init();
 
   base::TestSuite test_suite(argc, argv);
diff --git a/services/service_manager/runner/host/in_process_native_runner.cc b/services/service_manager/runner/host/in_process_native_runner.cc
new file mode 100644
index 0000000..156c233c
--- /dev/null
+++ b/services/service_manager/runner/host/in_process_native_runner.cc
@@ -0,0 +1,95 @@
+// Copyright 2014 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 "services/service_manager/runner/host/in_process_native_runner.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/process/process.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "services/service_manager/runner/host/native_library_runner.h"
+#include "services/service_manager/runner/host/out_of_process_native_runner.h"
+#include "services/service_manager/runner/init.h"
+
+namespace service_manager {
+
+InProcessNativeRunner::InProcessNativeRunner() : library_(nullptr) {}
+
+InProcessNativeRunner::~InProcessNativeRunner() {
+  // It is important to let the thread exit before unloading the DSO (when
+  // library_ is destructed), because the library may have registered
+  // thread-local data and destructors to run on thread termination.
+  if (thread_) {
+    DCHECK(thread_->HasBeenStarted());
+    DCHECK(!thread_->HasBeenJoined());
+    thread_->Join();
+  }
+}
+
+mojom::ServicePtr InProcessNativeRunner::Start(
+    const base::FilePath& library_path,
+    const Identity& target,
+    bool start_sandboxed,
+    const base::Callback<void(base::ProcessId)>& pid_available_callback,
+    const base::Closure& service_completed_callback) {
+  library_path_ = library_path;
+
+  DCHECK(!request_.is_pending());
+  mojom::ServicePtr client;
+  request_ = GetProxy(&client);
+
+  DCHECK(service_completed_callback_runner_.is_null());
+  service_completed_callback_runner_ = base::Bind(
+      &base::TaskRunner::PostTask, base::ThreadTaskRunnerHandle::Get(),
+      FROM_HERE, service_completed_callback);
+
+  DCHECK(!thread_);
+  std::string thread_name = "Service Thread";
+#if defined(OS_WIN)
+  thread_name = base::WideToUTF8(library_path_.BaseName().value());
+#endif
+  thread_.reset(new base::DelegateSimpleThread(this, thread_name));
+  thread_->Start();
+  pid_available_callback.Run(base::Process::Current().Pid());
+
+  return client;
+}
+
+void InProcessNativeRunner::Run() {
+  DVLOG(2) << "Loading/running Service in process from library: "
+           << library_path_.value()
+           << " thread id=" << base::PlatformThread::CurrentId();
+
+  // TODO(vtl): ScopedNativeLibrary doesn't have a .get() method!
+  base::NativeLibrary library = LoadNativeLibrary(library_path_);
+  library_.Reset(library);
+  // This hangs on Windows in the component build, so skip it since it's
+  // unnecessary.
+#if !(defined(COMPONENT_BUILD) && defined(OS_WIN))
+  CallLibraryEarlyInitialization(library);
+#endif
+  RunServiceInNativeLibrary(library, std::move(request_));
+  service_completed_callback_runner_.Run();
+  service_completed_callback_runner_.Reset();
+}
+
+std::unique_ptr<NativeRunner> InProcessNativeRunnerFactory::Create(
+    const base::FilePath& library_path) {
+  // Executables are always run in a new process.
+  if (!library_path.MatchesExtension(FILE_PATH_LITERAL(".library"))) {
+    return base::MakeUnique<OutOfProcessNativeRunner>(launch_process_runner_,
+                                                      nullptr);
+  }
+  return base::WrapUnique(new InProcessNativeRunner);
+}
+
+}  // namespace service_manager
diff --git a/services/service_manager/runner/host/in_process_native_runner.h b/services/service_manager/runner/host/in_process_native_runner.h
new file mode 100644
index 0000000..9da7beca
--- /dev/null
+++ b/services/service_manager/runner/host/in_process_native_runner.h
@@ -0,0 +1,70 @@
+// Copyright 2014 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 SERVICES_SERVICE_MANAGER_RUNNER_HOST_IN_PROCESS_NATIVE_RUNNER_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_HOST_IN_PROCESS_NATIVE_RUNNER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/scoped_native_library.h"
+#include "base/threading/simple_thread.h"
+#include "services/service_manager/native_runner.h"
+
+namespace base {
+class TaskRunner;
+}
+
+namespace service_manager {
+
+// An implementation of |NativeRunner| that loads/runs the given service (from
+// the file system) on a separate thread (in the current process).
+class InProcessNativeRunner : public NativeRunner,
+                              public base::DelegateSimpleThread::Delegate {
+ public:
+  InProcessNativeRunner();
+  ~InProcessNativeRunner() override;
+
+  // NativeRunner:
+  mojom::ServicePtr Start(
+      const base::FilePath& library_path,
+      const Identity& target,
+      bool start_sandboxed,
+      const base::Callback<void(base::ProcessId)>& pid_available_callback,
+      const base::Closure& service_completed_callback) override;
+
+ private:
+  // |base::DelegateSimpleThread::Delegate| method:
+  void Run() override;
+
+  base::FilePath library_path_;
+  mojom::ServiceRequest request_;
+  base::Callback<bool(void)> service_completed_callback_runner_;
+
+  base::ScopedNativeLibrary library_;
+  std::unique_ptr<base::DelegateSimpleThread> thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(InProcessNativeRunner);
+};
+
+class InProcessNativeRunnerFactory : public NativeRunnerFactory {
+ public:
+  explicit InProcessNativeRunnerFactory(base::TaskRunner* launch_process_runner)
+      : launch_process_runner_(launch_process_runner) {}
+  ~InProcessNativeRunnerFactory() override {}
+
+  std::unique_ptr<NativeRunner> Create(
+      const base::FilePath& library_path) override;
+
+ private:
+  base::TaskRunner* const launch_process_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(InProcessNativeRunnerFactory);
+};
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_IN_PROCESS_NATIVE_RUNNER_H_
diff --git a/services/service_manager/runner/host/in_process_native_runner_unittest.cc b/services/service_manager/runner/host/in_process_native_runner_unittest.cc
new file mode 100644
index 0000000..76596fa
--- /dev/null
+++ b/services/service_manager/runner/host/in_process_native_runner_unittest.cc
@@ -0,0 +1,16 @@
+// Copyright 2014 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 "services/service_manager/runner/host/in_process_native_runner.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace service_manager {
+
+TEST(InProcessNativeRunnerTest, NotStarted) {
+  InProcessNativeRunner runner;
+  // Shouldn't crash or DCHECK on destruction.
+}
+
+}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/standalone_service/linux_sandbox.cc b/services/service_manager/runner/host/linux_sandbox.cc
similarity index 98%
rename from services/service_manager/public/cpp/standalone_service/linux_sandbox.cc
rename to services/service_manager/runner/host/linux_sandbox.cc
index ae5b356..46fc62f 100644
--- a/services/service_manager/public/cpp/standalone_service/linux_sandbox.cc
+++ b/services/service_manager/runner/host/linux_sandbox.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/service_manager/public/cpp/standalone_service/linux_sandbox.h"
+#include "services/service_manager/runner/host/linux_sandbox.h"
 
 #include <fcntl.h>
 #include <sys/syscall.h>
diff --git a/services/service_manager/public/cpp/standalone_service/linux_sandbox.h b/services/service_manager/runner/host/linux_sandbox.h
similarity index 78%
rename from services/service_manager/public/cpp/standalone_service/linux_sandbox.h
rename to services/service_manager/runner/host/linux_sandbox.h
index df13dfd..a9e48045 100644
--- a/services/service_manager/public/cpp/standalone_service/linux_sandbox.h
+++ b/services/service_manager/runner/host/linux_sandbox.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_SERVICE_LINUX_SANDBOX_H_
-#define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_SERVICE_LINUX_SANDBOX_H_
+#ifndef SERVICES_SERVICE_MANAGER_RUNNER_HOST_LINUX_SANDBOX_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_HOST_LINUX_SANDBOX_H_
 
 #include <memory>
 
@@ -15,8 +15,7 @@
 
 namespace service_manager {
 
-// Encapsulates all tasks related to raising the sandbox for a standalone
-// service.
+// Encapsulates all tasks related to raising the sandbox for mojo runner.
 class LinuxSandbox {
  public:
   explicit LinuxSandbox(
@@ -49,4 +48,4 @@
 
 }  // namespace service_manager
 
-#endif  // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_SERVICE_LINUX_SANDBOX_H_
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_LINUX_SANDBOX_H_
diff --git a/services/service_manager/public/cpp/standalone_service/mach_broker.cc b/services/service_manager/runner/host/mach_broker.cc
similarity index 91%
rename from services/service_manager/public/cpp/standalone_service/mach_broker.cc
rename to services/service_manager/runner/host/mach_broker.cc
index 8f8c670..451a7fa 100644
--- a/services/service_manager/public/cpp/standalone_service/mach_broker.cc
+++ b/services/service_manager/runner/host/mach_broker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/service_manager/public/cpp/standalone_service/mach_broker.h"
+#include "services/service_manager/runner/host/mach_broker.h"
 
 #include "base/logging.h"
 #include "base/memory/singleton.h"
diff --git a/services/service_manager/public/cpp/standalone_service/mach_broker.h b/services/service_manager/runner/host/mach_broker.h
similarity index 86%
rename from services/service_manager/public/cpp/standalone_service/mach_broker.h
rename to services/service_manager/runner/host/mach_broker.h
index bb96e9f..5d139da 100644
--- a/services/service_manager/public/cpp/standalone_service/mach_broker.h
+++ b/services/service_manager/runner/host/mach_broker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_SERVICE_MACH_BROKER_H_
-#define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_SERVICE_MACH_BROKER_H_
+#ifndef SERVICES_SERVICE_MANAGER_RUNNER_HOST_MACH_BROKER_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_HOST_MACH_BROKER_H_
 
 #include "base/mac/mach_port_broker.h"
 
@@ -50,4 +50,4 @@
 
 }  // namespace service_manager
 
-#endif  // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_SERVICE_MACH_BROKER_H_
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_MACH_BROKER_H_
diff --git a/services/service_manager/runner/host/native_library_runner.cc b/services/service_manager/runner/host/native_library_runner.cc
new file mode 100644
index 0000000..fbb86644
--- /dev/null
+++ b/services/service_manager/runner/host/native_library_runner.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 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 "services/service_manager/runner/host/native_library_runner.h"
+
+#include <stddef.h>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "mojo/edk/embedder/entrypoints.h"
+#include "mojo/public/c/system/thunks.h"
+
+namespace service_manager {
+
+namespace {
+
+template <typename Thunks>
+bool SetThunks(Thunks (*make_thunks)(),
+               const char* function_name,
+               base::NativeLibrary library) {
+  typedef size_t (*SetThunksFn)(const Thunks* thunks);
+  SetThunksFn set_thunks = reinterpret_cast<SetThunksFn>(
+      base::GetFunctionPointerFromNativeLibrary(library, function_name));
+  if (!set_thunks)
+    return false;
+  Thunks thunks = make_thunks();
+  size_t expected_size = set_thunks(&thunks);
+  if (expected_size > sizeof(Thunks)) {
+    LOG(ERROR) << "Invalid library: expected " << function_name
+               << " to return thunks of size: " << expected_size;
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+base::NativeLibrary LoadNativeLibrary(const base::FilePath& path) {
+  DVLOG(2) << "Loading Service in process from library: " << path.value();
+
+  base::NativeLibraryLoadError error;
+  base::NativeLibrary library = base::LoadNativeLibrary(path, &error);
+  LOG_IF(ERROR, !library)
+      << "Failed to load library (path: " << path.value()
+      << " reason: " << error.ToString() << ")";
+  return library;
+}
+
+bool RunServiceInNativeLibrary(base::NativeLibrary library,
+                               mojom::ServiceRequest request) {
+  // Tolerate |library| being null, to make life easier for callers.
+  if (!library)
+    return false;
+
+// Thunks aren't needed/used in component build, since the thunked methods
+// just live in their own dynamically loaded library.
+#if !defined(COMPONENT_BUILD)
+  if (!SetThunks(&mojo::edk::MakeSystemThunks, "MojoSetSystemThunks",
+                 library)) {
+    LOG(ERROR) << "MojoSetSystemThunks not found";
+    return false;
+  }
+
+#if !defined(OS_WIN)
+  // On Windows, initializing base::CommandLine with null parameters gets the
+  // process's command line from the OS. Other platforms need it to be passed
+  // in. This needs to be passed in before the service initializes the command
+  // line, which is done as soon as it loads.
+  typedef void (*InitCommandLineArgs)(int, const char* const*);
+  InitCommandLineArgs init_command_line_args =
+      reinterpret_cast<InitCommandLineArgs>(
+          base::GetFunctionPointerFromNativeLibrary(library,
+                                                    "InitCommandLineArgs"));
+  if (init_command_line_args) {
+    int argc = 0;
+    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+    const char** argv = new const char*[cmd_line->argv().size()];
+    for (auto& arg : cmd_line->argv())
+      argv[argc++] = arg.c_str();
+    init_command_line_args(argc, argv);
+  }
+#endif
+
+#endif  // !defined(COMPONENT_BUILD)
+
+  typedef MojoResult (*ServiceMainFunction)(MojoHandle);
+  ServiceMainFunction main_function = reinterpret_cast<ServiceMainFunction>(
+      base::GetFunctionPointerFromNativeLibrary(library, "ServiceMain"));
+  if (!main_function) {
+    LOG(ERROR) << "ServiceMain not found";
+    return false;
+  }
+  // |ServiceMain()| takes ownership of the service handle.
+  MojoHandle handle = request.PassMessagePipe().release().value();
+  MojoResult result = main_function(handle);
+  if (result != MOJO_RESULT_OK) {
+    LOG(ERROR) << "ServiceMain returned error (result: " << result << ")";
+  }
+  return true;
+}
+
+}  // namespace service_manager
diff --git a/services/service_manager/runner/host/native_library_runner.h b/services/service_manager/runner/host/native_library_runner.h
new file mode 100644
index 0000000..9e19caa
--- /dev/null
+++ b/services/service_manager/runner/host/native_library_runner.h
@@ -0,0 +1,38 @@
+// Copyright 2014 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 SERVICES_SERVICE_MANAGER_RUNNER_HOST_NATIVE_LIBRARY_RUNNER_H_
+#define SERVICES_SERVICE_MANAGER_RUNNER_HOST_NATIVE_LIBRARY_RUNNER_H_
+
+#include "base/native_library.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "services/service_manager/public/interfaces/service.mojom.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace service_manager {
+
+// Loads the native Service from the DSO specified by |app_path|.
+// Returns the |base::NativeLibrary| for the service on success (or null on
+// failure).
+//
+// Note: The caller may choose to eventually unload the returned DSO. If so,
+// this should be done only after the thread on which |LoadNativeLibrary()|
+// and |RunServiceInNativeLibrary()| were called has terminated, so that any
+// thread-local destructors have been executed.
+base::NativeLibrary LoadNativeLibrary(const base::FilePath& app_path);
+
+// Runs the native Service from the DSO that was loaded using
+// |LoadNativeLibrary()|; this tolerates |library| being null. This should be
+// called on the same thread as |LoadNativeLibrary()|. Returns true if
+// |ServiceMain()| was called (even if it returns an error), and false
+// otherwise.
+bool RunServiceInNativeLibrary(base::NativeLibrary library,
+                               mojom::ServiceRequest request);
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_RUNNER_HOST_NATIVE_LIBRARY_RUNNER_H_
diff --git a/services/service_manager/runner/host/out_of_process_native_runner.cc b/services/service_manager/runner/host/out_of_process_native_runner.cc
index 0df2edf..9edd59d 100644
--- a/services/service_manager/runner/host/out_of_process_native_runner.cc
+++ b/services/service_manager/runner/host/out_of_process_native_runner.cc
@@ -16,33 +16,33 @@
 #include "base/task_runner.h"
 #include "services/service_manager/runner/common/client_util.h"
 #include "services/service_manager/runner/host/child_process_host.h"
+#include "services/service_manager/runner/host/in_process_native_runner.h"
 
 namespace service_manager {
 
 OutOfProcessNativeRunner::OutOfProcessNativeRunner(
-    const base::FilePath& service_path,
     base::TaskRunner* launch_process_runner,
     NativeRunnerDelegate* delegate)
-    : launch_process_runner_(launch_process_runner),
-      delegate_(delegate),
-      service_path_(service_path) {}
+    : launch_process_runner_(launch_process_runner), delegate_(delegate) {}
 
 OutOfProcessNativeRunner::~OutOfProcessNativeRunner() {
-  if (child_process_host_ && !service_path_.empty())
+  if (child_process_host_ && !app_path_.empty())
     child_process_host_->Join();
 }
 
 mojom::ServicePtr OutOfProcessNativeRunner::Start(
+    const base::FilePath& app_path,
     const Identity& target,
     bool start_sandboxed,
     const base::Callback<void(base::ProcessId)>& pid_available_callback,
     const base::Closure& app_completed_callback) {
+  app_path_ = app_path;
+
   DCHECK(app_completed_callback_.is_null());
   app_completed_callback_ = app_completed_callback;
 
   child_process_host_.reset(new ChildProcessHost(
-      launch_process_runner_, delegate_, start_sandboxed, target,
-      service_path_));
+      launch_process_runner_, delegate_, start_sandboxed, target, app_path));
   return child_process_host_->Start(
       target, pid_available_callback,
       base::Bind(&OutOfProcessNativeRunner::AppCompleted,
@@ -64,13 +64,12 @@
     base::TaskRunner* launch_process_runner,
     NativeRunnerDelegate* delegate)
     : launch_process_runner_(launch_process_runner), delegate_(delegate) {}
-
 OutOfProcessNativeRunnerFactory::~OutOfProcessNativeRunnerFactory() {}
 
 std::unique_ptr<NativeRunner> OutOfProcessNativeRunnerFactory::Create(
-    const base::FilePath& service_path) {
-  return base::MakeUnique<OutOfProcessNativeRunner>(
-      service_path, launch_process_runner_, delegate_);
+    const base::FilePath& app_path) {
+  return base::MakeUnique<OutOfProcessNativeRunner>(launch_process_runner_,
+                                                    delegate_);
 }
 
 }  // namespace service_manager
diff --git a/services/service_manager/runner/host/out_of_process_native_runner.h b/services/service_manager/runner/host/out_of_process_native_runner.h
index e2e8e4f4..df61565b 100644
--- a/services/service_manager/runner/host/out_of_process_native_runner.h
+++ b/services/service_manager/runner/host/out_of_process_native_runner.h
@@ -24,17 +24,17 @@
 class ChildProcessHost;
 class NativeRunnerDelegate;
 
-// An implementation of |NativeRunner| that runs a given service executable
-// in a separate, dedicated process.
+// An implementation of |NativeRunner| that loads/runs the given app (from the
+// file system) in a separate process (of its own).
 class OutOfProcessNativeRunner : public NativeRunner {
  public:
-  OutOfProcessNativeRunner(const base::FilePath& service_path,
-                           base::TaskRunner* launch_process_runner,
+  OutOfProcessNativeRunner(base::TaskRunner* launch_process_runner,
                            NativeRunnerDelegate* delegate);
   ~OutOfProcessNativeRunner() override;
 
   // NativeRunner:
   mojom::ServicePtr Start(
+      const base::FilePath& app_path,
       const Identity& identity,
       bool start_sandboxed,
       const base::Callback<void(base::ProcessId)>& pid_available_callback,
@@ -46,7 +46,7 @@
   base::TaskRunner* const launch_process_runner_;
   NativeRunnerDelegate* delegate_;
 
-  const base::FilePath service_path_;
+  base::FilePath app_path_;
   base::Closure app_completed_callback_;
 
   std::unique_ptr<ChildProcessHost> child_process_host_;
@@ -60,8 +60,7 @@
                                   NativeRunnerDelegate* delegate);
   ~OutOfProcessNativeRunnerFactory() override;
 
-  std::unique_ptr<NativeRunner> Create(
-      const base::FilePath& service_path) override;
+  std::unique_ptr<NativeRunner> Create(const base::FilePath& app_path) override;
 
  private:
   base::TaskRunner* const launch_process_runner_;
diff --git a/services/service_manager/service_manager.cc b/services/service_manager/service_manager.cc
index 5f815c3..fc516094 100644
--- a/services/service_manager/service_manager.cc
+++ b/services/service_manager/service_manager.cc
@@ -190,19 +190,15 @@
     StartWithService(std::move(service));
   }
 
-  bool StartWithFilePath(const base::FilePath& path) {
-    DCHECK(!service_);
-    DCHECK(!path.empty());
+  void StartWithFilePath(const base::FilePath& path) {
+    CHECK(!service_);
     runner_ = service_manager_->native_runner_factory_->Create(path);
-    if (!runner_)
-      return false;
     bool start_sandboxed = false;
     mojom::ServicePtr service = runner_->Start(
-        identity_, start_sandboxed,
+        path, identity_, start_sandboxed,
         base::Bind(&Instance::PIDAvailable, weak_factory_.GetWeakPtr()),
         base::Bind(&Instance::OnRunnerCompleted, weak_factory_.GetWeakPtr()));
     StartWithService(std::move(service));
-    return true;
   }
 
   mojom::RunningServiceInfoPtr CreateRunningServiceInfo() const {
@@ -951,10 +947,9 @@
         package_path = result->package_path;
       }
 
-      if (!instance->StartWithFilePath(package_path)) {
-        OnInstanceError(instance);
-        return;
-      }
+      // TODO(rockot): Find a way to block this code path for content but allow
+      // it for chrome_mash.
+      instance->StartWithFilePath(package_path);
     }
   }
 
diff --git a/services/service_manager/standalone/BUILD.gn b/services/service_manager/standalone/BUILD.gn
index cf01f75..f7633aff 100644
--- a/services/service_manager/standalone/BUILD.gn
+++ b/services/service_manager/standalone/BUILD.gn
@@ -38,7 +38,6 @@
     "//services/catalog:lib",
     "//services/service_manager",
     "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp/standalone_service",
     "//services/service_manager/runner/host:lib",
     "//services/tracing/public/cpp",
     "//services/tracing/public/interfaces",
diff --git a/services/service_manager/standalone/context.cc b/services/service_manager/standalone/context.cc
index 9800159..7af84b4 100644
--- a/services/service_manager/standalone/context.cc
+++ b/services/service_manager/standalone/context.cc
@@ -38,6 +38,7 @@
 #include "services/service_manager/connect_params.h"
 #include "services/service_manager/connect_util.h"
 #include "services/service_manager/runner/common/switches.h"
+#include "services/service_manager/runner/host/in_process_native_runner.h"
 #include "services/service_manager/runner/host/out_of_process_native_runner.h"
 #include "services/service_manager/standalone/tracer.h"
 #include "services/service_manager/switches.h"
@@ -47,7 +48,7 @@
 #include "services/tracing/public/interfaces/tracing.mojom.h"
 
 #if defined(OS_MACOSX)
-#include "services/service_manager/public/cpp/standalone_service/mach_broker.h"
+#include "services/service_manager/runner/host/mach_broker.h"
 #endif
 
 namespace service_manager {
@@ -136,10 +137,21 @@
 #endif
   }
 
-  std::unique_ptr<NativeRunnerFactory> runner_factory =
-      base::MakeUnique<OutOfProcessNativeRunnerFactory>(
-          blocking_pool_.get(),
-          init_params ? init_params->native_runner_delegate : nullptr);
+  std::unique_ptr<NativeRunnerFactory> runner_factory;
+  if (command_line.HasSwitch(switches::kSingleProcess)) {
+#if defined(COMPONENT_BUILD)
+    LOG(ERROR) << "Running Mojo in single process component build, which isn't "
+               << "supported because statics in apps interact. Use static build"
+               << " or don't pass --single-process.";
+#endif
+    runner_factory.reset(
+        new InProcessNativeRunnerFactory(blocking_pool_.get()));
+  } else {
+    NativeRunnerDelegate* native_runner_delegate = init_params ?
+        init_params->native_runner_delegate : nullptr;
+    runner_factory.reset(new OutOfProcessNativeRunnerFactory(
+        blocking_pool_.get(), native_runner_delegate));
+  }
   std::unique_ptr<catalog::Store> store;
   if (init_params)
     store = std::move(init_params->catalog_store);
diff --git a/services/service_manager/standalone/desktop/main_helper.cc b/services/service_manager/standalone/desktop/main_helper.cc
index 45d2f03..d3f11c8 100644
--- a/services/service_manager/standalone/desktop/main_helper.cc
+++ b/services/service_manager/standalone/desktop/main_helper.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "services/service_manager/runner/common/switches.h"
+#include "services/service_manager/runner/host/child_process.h"
 #include "services/service_manager/runner/init.h"
 #include "services/service_manager/standalone/desktop/launcher_process.h"
 
@@ -28,6 +29,8 @@
 
 int StandaloneServiceManagerMain(int argc, char** argv) {
   base::CommandLine::Init(argc, argv);
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
 
   base::AtExitManager at_exit;
   InitializeLogging();
@@ -37,6 +40,9 @@
   base::RouteStdioToConsole(false);
 #endif
 
+  if (command_line.HasSwitch(switches::kChildProcess))
+    return ChildProcessMain();
+
   return LauncherProcessMain();
 }
 
diff --git a/services/service_manager/switches.cc b/services/service_manager/switches.cc
index c8b9d2f..54221de 100644
--- a/services/service_manager/switches.cc
+++ b/services/service_manager/switches.cc
@@ -13,5 +13,8 @@
 // Disables the sandbox for debugging.
 const char kNoSandbox[] = "no-sandbox";
 
+// Load apps in a single processes.
+const char kSingleProcess[] = "single-process";
+
 }  // namespace switches
 }  // namespace service_manager
diff --git a/services/service_manager/switches.h b/services/service_manager/switches.h
index fc12923ee..35abc69 100644
--- a/services/service_manager/switches.h
+++ b/services/service_manager/switches.h
@@ -12,6 +12,7 @@
 // alongside the definition of their values in the .cc file.
 extern const char kEnableTracing[];
 extern const char kNoSandbox[];
+extern const char kSingleProcess[];
 
 }  // namespace switches
 }  // namespace service_manager
diff --git a/services/service_manager/tests/connect/BUILD.gn b/services/service_manager/tests/connect/BUILD.gn
index 10bc623..ab9f411ff 100644
--- a/services/service_manager/tests/connect/BUILD.gn
+++ b/services/service_manager/tests/connect/BUILD.gn
@@ -162,7 +162,7 @@
     "//base",
     "//build/win:default_exe_manifest",
     "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp/standalone_service:main",
+    "//services/service_manager/runner/child:test_native_main",
   ]
 
   data_deps = [
diff --git a/services/service_manager/tests/connect/connect_test_exe.cc b/services/service_manager/tests/connect/connect_test_exe.cc
index 128eacd..60de89e96 100644
--- a/services/service_manager/tests/connect/connect_test_exe.cc
+++ b/services/service_manager/tests/connect/connect_test_exe.cc
@@ -2,17 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-
+#include "base/at_exit.h"
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/c/main.h"
+#include "services/service_manager/public/cpp/connection.h"
+#include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_factory.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context.h"
-#include "services/service_manager/public/cpp/service_runner.h"
+#include "services/service_manager/runner/child/test_native_main.h"
+#include "services/service_manager/runner/init.h"
 #include "services/service_manager/tests/connect/connect_test.mojom.h"
 
 using service_manager::test::mojom::ConnectTestService;
@@ -45,7 +47,6 @@
   void GetTitle(const GetTitleCallback& callback) override {
     callback.Run("connect_test_exe");
   }
-
   void GetInstance(const GetInstanceCallback& callback) override {
     callback.Run(context()->identity().instance());
   }
@@ -55,9 +56,12 @@
   DISALLOW_COPY_AND_ASSIGN(Target);
 };
 
-}  // namespac
+}  // namespace
 
-MojoResult ServiceMain(MojoHandle service_request_handle) {
-  service_manager::ServiceRunner runner(new Target);
-  return runner.Run(service_request_handle);
+int main(int argc, char** argv) {
+  base::AtExitManager at_exit;
+  base::CommandLine::Init(argc, argv);
+
+  service_manager::InitializeLogging();
+  return service_manager::TestNativeMain(base::MakeUnique<Target>());
 }
diff --git a/services/service_manager/tests/lifecycle/BUILD.gn b/services/service_manager/tests/lifecycle/BUILD.gn
index eb3bd461..0e65b67e 100644
--- a/services/service_manager/tests/lifecycle/BUILD.gn
+++ b/services/service_manager/tests/lifecycle/BUILD.gn
@@ -149,7 +149,7 @@
     "//base",
     "//build/win:default_exe_manifest",
     "//services/service_manager/public/cpp:sources",
-    "//services/service_manager/public/cpp/standalone_service:main",
+    "//services/service_manager/runner/child:test_native_main",
   ]
 
   data_deps = [
diff --git a/services/service_manager/tests/lifecycle/lifecycle_exe.cc b/services/service_manager/tests/lifecycle/lifecycle_exe.cc
index f5ab4cd..a7ff9f9e 100644
--- a/services/service_manager/tests/lifecycle/lifecycle_exe.cc
+++ b/services/service_manager/tests/lifecycle/lifecycle_exe.cc
@@ -2,11 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/service_manager/public/c/main.h"
-#include "services/service_manager/public/cpp/service_runner.h"
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "services/service_manager/public/cpp/connection.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/runner/child/test_native_main.h"
+#include "services/service_manager/runner/init.h"
 #include "services/service_manager/tests/lifecycle/app_client.h"
 
-MojoResult ServiceMain(MojoHandle service_request_handle) {
-  service_manager::ServiceRunner runner(new service_manager::test::AppClient);
-  return runner.Run(service_request_handle);
+int main(int argc, char** argv) {
+  base::AtExitManager at_exit;
+  base::CommandLine::Init(argc, argv);
+
+  service_manager::InitializeLogging();
+  return service_manager::TestNativeMain(
+      base::MakeUnique<service_manager::test::AppClient>());
 }
diff --git a/services/service_manager/tests/lifecycle/package.cc b/services/service_manager/tests/lifecycle/package.cc
index 5dc4edce..7287409 100644
--- a/services/service_manager/tests/lifecycle/package.cc
+++ b/services/service_manager/tests/lifecycle/package.cc
@@ -97,20 +97,22 @@
   DISALLOW_COPY_AND_ASSIGN(PackagedApp);
 };
 
-class Package : public service_manager::ForwardingService,
+class Package : public service_manager::Service,
                 public service_manager::InterfaceFactory<
                     service_manager::mojom::ServiceFactory>,
                 public service_manager::mojom::ServiceFactory {
  public:
-  Package() : ForwardingService(&app_client_) {}
+  Package() {}
   ~Package() override {}
 
  private:
-  // ForwardingService:
+  // service_manager::Service:
+  void OnStart() override { app_client_.OnStart(); }
+
   bool OnConnect(const service_manager::ServiceInfo& remote_info,
                  service_manager::InterfaceRegistry* registry) override {
     registry->AddInterface<service_manager::mojom::ServiceFactory>(this);
-    return ForwardingService::OnConnect(remote_info, registry);
+    return app_client_.OnConnect(remote_info, registry);
   }
 
   // service_manager::InterfaceFactory<service_manager::mojom::ServiceFactory>:
diff --git a/services/service_manager/tests/service_manager/BUILD.gn b/services/service_manager/tests/service_manager/BUILD.gn
index d494b1b8..e8960a5 100644
--- a/services/service_manager/tests/service_manager/BUILD.gn
+++ b/services/service_manager/tests/service_manager/BUILD.gn
@@ -59,7 +59,7 @@
     "//base",
     "//build/win:default_exe_manifest",
     "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp/standalone_service:main",
+    "//services/service_manager/runner/child:test_native_main",
   ]
 
   data_deps = [
@@ -99,9 +99,9 @@
     "//build/win:default_exe_manifest",
     "//mojo/edk/system",
     "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp/standalone_service:main",
     "//services/service_manager/public/interfaces",
     "//services/service_manager/runner:init",
+    "//services/service_manager/runner/child:test_native_main",
     "//services/service_manager/runner/common",
   ]
 
diff --git a/services/service_manager/tests/service_manager/embedder.cc b/services/service_manager/tests/service_manager/embedder.cc
index 9cabede..af5ad9e6 100644
--- a/services/service_manager/tests/service_manager/embedder.cc
+++ b/services/service_manager/tests/service_manager/embedder.cc
@@ -18,6 +18,8 @@
 #include "services/service_manager/public/cpp/service_runner.h"
 #include "services/service_manager/public/interfaces/service_factory.mojom.h"
 #include "services/service_manager/public/interfaces/service_manager.mojom.h"
+#include "services/service_manager/runner/child/test_native_main.h"
+#include "services/service_manager/runner/init.h"
 
 namespace {
 
diff --git a/services/service_manager/tests/service_manager/target.cc b/services/service_manager/tests/service_manager/target.cc
index 02d12ec2..4efef83 100644
--- a/services/service_manager/tests/service_manager/target.cc
+++ b/services/service_manager/tests/service_manager/target.cc
@@ -6,12 +6,12 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "services/service_manager/public/c/main.h"
 #include "services/service_manager/public/cpp/connection.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context.h"
-#include "services/service_manager/public/cpp/service_runner.h"
+#include "services/service_manager/runner/child/test_native_main.h"
+#include "services/service_manager/runner/init.h"
 #include "services/service_manager/tests/service_manager/service_manager_unittest.mojom.h"
 
 using service_manager::test::mojom::CreateInstanceTestPtr;
@@ -42,7 +42,10 @@
 
 }  // namespace
 
-MojoResult ServiceMain(MojoHandle service_request_handle) {
-  service_manager::ServiceRunner runner(new Target);
-  return runner.Run(service_request_handle);
+int main(int argc, char** argv) {
+  base::AtExitManager at_exit;
+  base::CommandLine::Init(argc, argv);
+
+  service_manager::InitializeLogging();
+  return service_manager::TestNativeMain(base::MakeUnique<Target>());
 }
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index b384a93..cb51c87 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -10,6 +10,8 @@
     "main.cc",
   ]
 
+  avoid_runner_cycle = true
+
   deps = [
     ":lib",
     "//mojo/public/cpp/system",
diff --git a/services/ui/BUILD.gn b/services/ui/BUILD.gn
index f8d88227..416f4750 100644
--- a/services/ui/BUILD.gn
+++ b/services/ui/BUILD.gn
@@ -24,7 +24,6 @@
   ]
 
   deps = [
-    ":copy_gl_libraries",
     ":lib",
     ":resources_100",
     ":resources_200",
@@ -33,8 +32,11 @@
     "//services/tracing/public/interfaces",
   ]
 
+  if (is_win) {
+    deps += [ ":copy_gl_libraries" ]
+  }
+
   data_deps = [
-    ":copy_gl_libraries",
     ":manifest",
     "//services/ui/ime/test_ime_driver",
   ]
@@ -51,13 +53,9 @@
   source = "manifest.json"
 }
 
-copy("copy_gl_libraries") {
-  deps = [
-    "//third_party/mesa:osmesa",
-  ]
-
-  if (is_win) {
-    deps += [
+if (is_win) {
+  copy("copy_gl_libraries") {
+    deps = [
       "//third_party/angle:libEGL",
       "//third_party/angle:libGLESv2",
     ]
@@ -65,19 +63,12 @@
     sources = [
       "$root_shlib_dir/libEGL.dll",
       "$root_shlib_dir/libGLESv2.dll",
-      "$root_shlib_dir/osmesa.dll",
     ]
-  } else if (is_android || is_linux) {
-    sources = [
-      "$root_shlib_dir/libosmesa.so",
-    ]
-  } else {
-    sources = []
-  }
 
-  outputs = [
-    "$root_out_dir/$packages_directory/ui/{{source_file_part}}",
-  ]
+    outputs = [
+      "$root_out_dir/$packages_directory/ui/{{source_file_part}}",
+    ]
+  }
 }
 
 source_set("lib") {
diff --git a/services/ui/gpu/gpu_main.cc b/services/ui/gpu/gpu_main.cc
index 72d615fd..948106e 100644
--- a/services/ui/gpu/gpu_main.cc
+++ b/services/ui/gpu/gpu_main.cc
@@ -15,14 +15,6 @@
 
 namespace {
 
-#if defined(OS_WIN)
-std::unique_ptr<base::MessagePump> CreateMessagePumpWin() {
-  base::MessagePumpForGpu::InitFactory();
-  return base::MessageLoop::CreateMessagePumpForType(
-      base::MessageLoop::TYPE_UI);
-}
-#endif  // defined(OS_WIN)
-
 #if defined(USE_X11)
 std::unique_ptr<base::MessagePump> CreateMessagePumpX11() {
   // TODO(sad): This should create a TYPE_UI message pump, and create a
@@ -50,7 +42,7 @@
   base::Thread::Options thread_options;
 
 #if defined(OS_WIN)
-  thread_options.message_pump_factory = base::Bind(&CreateMessagePumpWin);
+  thread_options.message_loop_type = base::MessageLoop::TYPE_DEFAULT;
 #elif defined(USE_X11)
   thread_options.message_pump_factory = base::Bind(&CreateMessagePumpX11);
 #elif defined(USE_OZONE)
diff --git a/services/ui/ws/window_manager_client_unittest.cc b/services/ui/ws/window_manager_client_unittest.cc
index 0697239c..67fcb63 100644
--- a/services/ui/ws/window_manager_client_unittest.cc
+++ b/services/ui/ws/window_manager_client_unittest.cc
@@ -730,6 +730,7 @@
   aura::WindowTreeClient second_client(connector(), this);
   second_client.ConnectViaWindowTreeFactory();
   aura::WindowTreeHostMus window_tree_host_in_second_client(&second_client);
+  window_tree_host_in_second_client.InitHost();
   aura::Window* second_client_child = NewVisibleWindow(
       window_tree_host_in_second_client.window(), &second_client);
   std::unique_ptr<aura::WindowPortMus> window_port_mus =
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 5e303fe..c5066b8 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -866,6 +866,38 @@
         "test": "mojo_public_system_unittests"
       },
       {
+        "override_isolate_target": "mojo_runner_host_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:3ff24775a900b675866fbcacf2a8f98a18b2a16a"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "android_devices": "4",
+              "device_os": "MMB29Q",
+              "device_type": "bullhead"
+            }
+          ],
+          "hard_timeout": 60,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "mojo_runner_host_unittests"
+      },
+      {
         "override_isolate_target": "mojo_system_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping-expected.txt
index a983517f3..92abcdc 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping-expected.txt
@@ -1,23 +1,6 @@
 Tests file system project mappings.
 
 
-Running: testAutomaticMapping
-Adding file system.
-Adding network resource.
-UISourceCode uri to url mappings:
-    file:///var/www/html/foo.js -> 
-    file:///var/www/bar.js -> 
-Adding mapping between network and file system resources.
-Emulate reloading inspector.
-UISourceCode uri to url mappings:
-    file:///var/www/html/foo.js -> http://127.0.0.1:8000/inspector/resources/html/foo.js
-    file:///var/www/bar.js -> http://127.0.0.1:8000/inspector/resources/bar.js
-Removing mapping between network and file system resources.
-Emulate reloading inspector.
-UISourceCode uri to url mappings:
-    file:///var/www/html/foo.js -> 
-    file:///var/www/bar.js -> 
-
 Running: testProjectBasedMapping
 Adding file system.
 UISourceCode uri to url mappings:
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping.html b/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping.html
index d5097ad..0b71510 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping.html
@@ -2,6 +2,7 @@
 <head>
 <script src="inspector-test.js"></script>
 <script src="debugger-test.js"></script>
+<script src="persistence/persistence-test.js"></script>
 <script src="isolated-filesystem-test.js"></script>
 <script>
 function addScript(url)
@@ -14,7 +15,6 @@
 function test()
 {
     var target = InspectorTest.mainTarget;
-    var persistence = Persistence.persistence;
     var fileSystemProjectId = Persistence.FileSystemWorkspaceBinding.projectId("file:///var/www");
 
     function dumpFileSystemUISourceCodesMappings()
@@ -22,78 +22,19 @@
         var uiSourceCodes = Workspace.workspace.project(fileSystemProjectId).uiSourceCodes();
         InspectorTest.addResult("UISourceCode uri to url mappings:");
         for (var uiSourceCode of uiSourceCodes) {
-            var binding = persistence.binding(uiSourceCode);
+            var binding = Persistence.persistence.binding(uiSourceCode);
             var url = binding ? binding.network.url() : "";
             InspectorTest.addResult("    " + uiSourceCode.url() + " -> " + url);
         }
     }
 
     InspectorTest.runTestSuite([
-        function testAutomaticMapping(next)
-        {
-            InspectorTest.addResult("Adding file system.");
-            var fs = new InspectorTest.TestFileSystem("file:///var/www");
-            fs.root.mkdir("html").addFile("foo.js", "<foo content>");
-            fs.root.addFile("bar.js", "<bar content>");
-            fs.reportCreated(fileSystemCreated1);
-            var networkUISourceCode;
-
-            function fileSystemCreated1()
-            {
-                InspectorTest.addResult("Adding network resource.");
-                InspectorTest.waitForUISourceCode(scriptsAdded, "resources/bar.js");
-                InspectorTest.evaluateInPage("addScript('resources/html/foo.js')");
-                InspectorTest.evaluateInPage("addScript('resources/bar.js')");
-            }
-
-            function scriptsAdded()
-            {
-                dumpFileSystemUISourceCodesMappings();
-
-                var uiSourceCode = Workspace.workspace.uiSourceCode(fileSystemProjectId, "file:///var/www/html/foo.js");
-                networkUISourceCode = Workspace.workspace.uiSourceCode(Bindings.NetworkProject.projectId(target, InspectorTest.resourceTreeModel.mainFrame, false), "http://127.0.0.1:8000/inspector/resources/html/foo.js");
-                InspectorTest.addResult("Adding mapping between network and file system resources.");
-
-                var fileSystemPath = Persistence.FileSystemWorkspaceBinding.fileSystemPath(uiSourceCode.project().id());
-                Workspace.fileSystemMapping.addMappingForResource(networkUISourceCode.url(), fileSystemPath, uiSourceCode.url());
-                var setting = JSON.stringify(Workspace.fileSystemMapping._fileSystemMappingSetting.get());
-
-                InspectorTest.addResult("Emulate reloading inspector.");
-                fs.reportRemoved();
-                Workspace.fileSystemMapping._fileSystemMappingSetting.set(JSON.parse(setting));
-                Workspace.fileSystemMapping._loadFromSettings();
-                fs.reportCreated(fileSystemCreated2);
-            }
-
-            function fileSystemCreated2()
-            {
-                dumpFileSystemUISourceCodesMappings();
-
-                InspectorTest.addResult("Removing mapping between network and file system resources.");
-                var uiSourceCode = Workspace.workspace.uiSourceCode(fileSystemProjectId, "file:///var/www/html/foo.js");
-                Workspace.fileSystemMapping.removeMappingForURL(uiSourceCode.url());
-
-                InspectorTest.addResult("Emulate reloading inspector.");
-                fs.reportRemoved();
-                fs.reportCreated(fileSystemCreated3);
-            }
-
-            function fileSystemCreated3()
-            {
-                dumpFileSystemUISourceCodesMappings();
-
-                Workspace.fileSystemMapping.removeMappingForURL(networkUISourceCode.url());
-                fs.reportRemoved();
-                next();
-            }
-        },
-
         function testProjectBasedMapping(next)
         {
             InspectorTest.addResult("Adding file system.");
             var fs = new InspectorTest.TestFileSystem("file:///var/www");
-            fs.root.mkdir("html").addFile("foo.js", "<foo content>");
-            fs.root.mkdir("html2").addFile("bar.js", "<bar content>");
+            fs.root.mkdir("html").addFile("foo.js", "var foo = 1;");
+            fs.root.mkdir("html2").addFile("bar.js", "var bar = 2;");
             fs.root.addFile(".devtools", JSON.stringify({ mappings: [ { folder: "/html/", url: "http://127.0.0.1:8000/inspector/resources/html/" }, { folder: "/html2/", url: "http://127.0.0.1:8000/inspector/resources/html2/" } ]}));
             fs.reportCreated(fileSystemCreated);
 
@@ -101,10 +42,13 @@
             {
                 InspectorTest.evaluateInPage("addScript('resources/html/foo.js')");
                 InspectorTest.evaluateInPage("addScript('resources/html2/bar.js')");
-                InspectorTest.waitForUISourceCode(scriptsAdded, "resources/html2/bar.js");
+                Promise.all([
+                    InspectorTest.waitForBinding("foo.js"),
+                    InspectorTest.waitForBinding("bar.js")
+                ]).then(onBindings);
             }
 
-            function scriptsAdded()
+            function onBindings()
             {
                 dumpFileSystemUISourceCodesMappings();
                 fs.reportRemoved();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-bind-dirty-sourcecode-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-bind-dirty-sourcecode-expected.txt
index 83440705..361d13db 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-bind-dirty-sourcecode-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-bind-dirty-sourcecode-expected.txt
@@ -4,7 +4,9 @@
 Running: waitForUISourceCodes
 
 Running: addFileSystemMapping
-
-Running: dumpConsoleMessages
-foo.js can not be persisted to file system due to unsaved changes.
+Failed to create binding: {
+       network: http://127.0.0.1:8000/inspector/persistence/resources/foo.js
+    fileSystem: file:///var/www/inspector/persistence/resources/foo.js
+    exactMatch: false
+}
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-bind-dirty-sourcecode.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-bind-dirty-sourcecode.html
index a201ed2..aa8c1861 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-bind-dirty-sourcecode.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-do-not-bind-dirty-sourcecode.html
@@ -26,14 +26,14 @@
 
         function addFileSystemMapping(next)
         {
+            InspectorTest.addSniffer(Persistence.Persistence.prototype, '_prevalidationFailedForTest', onPrevalidationFailed);
             Workspace.fileSystemMapping.addFileMapping(fs.fileSystemPath, "http://127.0.0.1:8000", "/");
-            next();
-        },
 
-        function dumpConsoleMessages(next)
-        {
-            InspectorTest.dumpConsoleMessages();
-            next();
+            function onPrevalidationFailed(binding)
+            {
+                InspectorTest.addResult("Failed to create binding: " + binding);
+                next();
+            }
         },
     ]);
 };
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs-expected.txt
index 68956628..ec3c0669 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs-expected.txt
@@ -1,4 +1,4 @@
-Verify that tabs get merged and split when binding is added and removed.
+Verify that tabs get merged when binding is added and removed.
 
 
 Running: addFileSystem
@@ -13,7 +13,7 @@
 SourceFrame: file:///var/www/inspector/persistence/resources/foo.js
     selection: {"startLine":0,"startColumn":0,"endLine":0,"endColumn":0}
     firstVisibleLine: 0
-    isDirty: true
+    isDirty: false
 Opened tabs: 
     file:///var/www/inspector/persistence/resources/foo.js
     http://127.0.0.1:8000/inspector/persistence/resources/foo.js
@@ -24,7 +24,7 @@
 SourceFrame: file:///var/www/inspector/persistence/resources/foo.js
     selection: {"startLine":2,"startColumn":0,"endLine":2,"endColumn":5}
     firstVisibleLine: 2
-    isDirty: true
+    isDirty: false
 
 Running: removeFileMapping
 Opened tabs: 
@@ -32,5 +32,5 @@
 SourceFrame: file:///var/www/inspector/persistence/resources/foo.js
     selection: {"startLine":2,"startColumn":0,"endLine":2,"endColumn":5}
     firstVisibleLine: 2
-    isDirty: true
+    isDirty: false
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs.html
index 1386092..1f54abd2 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs.html
@@ -38,15 +38,9 @@
         function openFileSystemTab(next)
         {
             InspectorTest.waitForUISourceCode("foo.js", Workspace.projectTypes.FileSystem)
-                .then(onFileSystemSourceCode)
+                .then(code => InspectorTest.showUISourceCodePromise(code))
                 .then(onFileSystemTab);
 
-            function onFileSystemSourceCode(code)
-            {
-                code.setWorkingCopy("\n\nwindow.foo = ()=>'foo2';");
-                return InspectorTest.showUISourceCodePromise(code);
-            }
-
             function onFileSystemTab(sourceFrame)
             {
                 fileSystemSourceFrame = sourceFrame;
@@ -108,6 +102,6 @@
 </script>
 </head>
 <body onload="runTest()">
-<p>Verify that tabs get merged and split when binding is added and removed.</p>
+<p>Verify that tabs get merged when binding is added and removed.</p>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-nodejs-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-nodejs-expected.txt
index b42f8019..d63e67df 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-nodejs-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-nodejs-expected.txt
@@ -40,7 +40,6 @@
     (function (exports, require, module, __filename, __dirname) { 
     
     var express = require("express");
-    network();
     workingCopy1();
     //TODO
     });
@@ -49,7 +48,6 @@
     #!/usr/bin/env node
     
     var express = require("express");
-    network();
     workingCopy1();
     //TODO
 
@@ -76,7 +74,6 @@
     (function (exports, require, module, __filename, __dirname) { 
     
     var express = require("express");
-    filesystem();
     workingCopy2();
     //TODO
     });
@@ -85,7 +82,6 @@
     #!/usr/bin/env node
     
     var express = require("express");
-    filesystem();
     workingCopy2();
     //TODO
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-nodejs.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-nodejs.html
index 53350bc..04dfca8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-nodejs.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-nodejs.html
@@ -52,9 +52,9 @@
     var testSuite = [
         function addNetworkUISourceCodeRevision(next)
         {
-            nodeContent = nodeContent.replace("//TODO", "network();\n//TODO");
+            var newContent = nodeContent.replace("//TODO", "network();\n//TODO");
             InspectorTest.addSniffer(Persistence.Persistence.prototype, "_contentSyncedForTest", onSynced);
-            binding.network.addRevision(nodeContent);
+            binding.network.addRevision(newContent);
 
             function onSynced()
             {
@@ -65,9 +65,9 @@
 
         function setNetworkUISourceCodeWorkingCopy(next)
         {
-            nodeContent = nodeContent.replace("//TODO", "workingCopy1();\n//TODO");
+            var newContent = nodeContent.replace("//TODO", "workingCopy1();\n//TODO");
             InspectorTest.addSniffer(Persistence.Persistence.prototype, "_contentSyncedForTest", onSynced);
-            binding.network.setWorkingCopy(nodeContent);
+            binding.network.setWorkingCopy(newContent);
 
             function onSynced()
             {
@@ -78,9 +78,9 @@
 
         function changeFileSystemFile(next)
         {
-            fsContent = fsContent.replace("//TODO", "filesystem();\n//TODO");
+            var newContent = fsContent.replace("//TODO", "filesystem();\n//TODO");
             InspectorTest.addSniffer(Persistence.Persistence.prototype, "_contentSyncedForTest", onSynced);
-            fsEntry.setContent(fsContent);
+            fsEntry.setContent(newContent);
 
             function onSynced()
             {
@@ -91,9 +91,9 @@
 
         function setFileSystemUISourceCodeWorkingCopy(next)
         {
-            nodeContent = fsContent.replace("//TODO", "workingCopy2();\n//TODO");
+            var newContent = fsContent.replace("//TODO", "workingCopy2();\n//TODO");
             InspectorTest.addSniffer(Persistence.Persistence.prototype, "_contentSyncedForTest", onSynced);
-            binding.fileSystem.setWorkingCopy(nodeContent);
+            binding.fileSystem.setWorkingCopy(newContent);
 
             function onSynced()
             {
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-test.js
index 6d8b302..7cef5cc 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-test.js
@@ -3,6 +3,8 @@
 InspectorTest.preloadModule("persistence");
 InspectorTest.preloadModule("sources");
 
+Runtime.experiments.enableForTest("persistenceValidation");
+
 Persistence.PersistenceBinding.prototype.toString = function()
 {
     var lines = [
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/file-system-project-live-edit.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger/file-system-project-live-edit.html
index a1d8ba2..98f0e68 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger/file-system-project-live-edit.html
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/file-system-project-live-edit.html
@@ -1,6 +1,7 @@
 <html>
 <head>
 <script src="../../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../../http/tests/inspector/persistence/persistence-test.js"></script>
 <script src="../../../http/tests/inspector/debugger-test.js"></script>
 <script src="../../../http/tests/inspector/isolated-filesystem-test.js"></script>
 <script src="../../../http/tests/inspector/live-edit-test.js"></script>
@@ -26,7 +27,8 @@
 
             function fileSystemCreated()
             {
-                InspectorTest.evaluateInPage("addScript()", didAddScript);
+                InspectorTest.evaluateInPage("addScript()");
+                InspectorTest.waitForBinding("edit-me.js").then(didAddScript);
             }
 
             function didAddScript()
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-8-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-8-expected.txt
new file mode 100644
index 0000000..6c3f1cfd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-8-expected.txt
@@ -0,0 +1,17 @@
+Verifies JavaScript pretty-printing functionality.
+
+
+Running: asyncAwaitSupport
+====== 8< ------
+async function foo() {
+    return await Promise.resolve(1);
+}
+
+------ >8 ======
+Correct mapping for <async>
+Correct mapping for <function>
+Correct mapping for <foo>
+Correct mapping for <return>
+Correct mapping for <Promise>
+Correct mapping for <resolve>
+
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-8.html b/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-8.html
new file mode 100644
index 0000000..b970ebc8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-8.html
@@ -0,0 +1,29 @@
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/debugger-test.js"></script>
+<script src="../../http/tests/inspector/sources-test.js"></script>
+
+<script>
+
+function test()
+{
+    var testJSFormatter = InspectorTest.testPrettyPrint.bind(InspectorTest, "text/javascript");
+
+    InspectorTest.runTestSuite([
+        function asyncAwaitSupport(next)
+        {
+            var mappingQueries = ["async", "function", "foo", "return", "Promise", "resolve"];
+            testJSFormatter("async function foo() {return await Promise.resolve(1);}", mappingQueries, next);
+        },
+    ]);
+}
+
+</script>
+
+</head>
+
+<body onload="runTest()">
+<p>Verifies JavaScript pretty-printing functionality.</p>
+</body>
+</html>
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/AcornTokenizer.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/AcornTokenizer.js
index c306fb6..395c8e5 100644
--- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/AcornTokenizer.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/AcornTokenizer.js
@@ -11,7 +11,7 @@
   constructor(content) {
     this._content = content;
     this._comments = [];
-    this._tokenizer = acorn.tokenizer(this._content, {ecmaVersion: 7, onComment: this._comments});
+    this._tokenizer = acorn.tokenizer(this._content, {ecmaVersion: 8, onComment: this._comments});
     this._lineEndings = this._content.computeLineEndings();
     this._lineNumber = 0;
     this._tokenLineStart = 0;
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/ESTreeWalker.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/ESTreeWalker.js
index 1f9398b..38b0da84 100644
--- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/ESTreeWalker.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/ESTreeWalker.js
@@ -91,6 +91,7 @@
 
 /** @enum {!Array.<string>} */
 FormatterWorker.ESTreeWalker._walkOrder = {
+  'AwaitExpression': ['arguments'],
   'ArrayExpression': ['elements'],
   'ArrowFunctionExpression': ['params', 'body'],
   'AssignmentExpression': ['left', 'right'],
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/FormatterWorker.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/FormatterWorker.js
index 9fa7adc9..45ced4b34 100644
--- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/FormatterWorker.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/FormatterWorker.js
@@ -97,7 +97,7 @@
  * @param {string} content
  */
 FormatterWorker.evaluatableJavaScriptSubstring = function(content) {
-  var tokenizer = acorn.tokenizer(content, {ecmaVersion: 7});
+  var tokenizer = acorn.tokenizer(content, {ecmaVersion: 8});
   var result = '';
   try {
     var token = tokenizer.getToken();
@@ -139,7 +139,7 @@
  * @param {string} content
  */
 FormatterWorker.javaScriptIdentifiers = function(content) {
-  var root = acorn.parse(content, {ranges: false, ecmaVersion: 6});
+  var root = acorn.parse(content, {ranges: false, ecmaVersion: 8});
 
   /** @type {!Array<!ESTree.Node>} */
   var identifiers = [];
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptFormatter.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptFormatter.js
index 85ed624f..640ddb4d 100644
--- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptFormatter.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptFormatter.js
@@ -51,7 +51,7 @@
     this._content = text.substring(this._fromOffset, this._toOffset);
     this._lastLineNumber = 0;
     this._tokenizer = new FormatterWorker.AcornTokenizer(this._content);
-    var ast = acorn.parse(this._content, {ranges: false, ecmaVersion: 7});
+    var ast = acorn.parse(this._content, {ranges: false, ecmaVersion: 8});
     var walker = new FormatterWorker.ESTreeWalker(this._beforeVisit.bind(this), this._afterVisit.bind(this));
     walker.walk(ast);
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index 52c6481..c7e58b5b 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -104,6 +104,7 @@
     Runtime.experiments.register('liveSASS', 'Live SASS');
     Runtime.experiments.register('nodeDebugging', 'Node debugging', true);
     Runtime.experiments.register('persistence2', 'Persistence 2.0');
+    Runtime.experiments.register('persistenceValidation', 'Validate persistence bindings');
     Runtime.experiments.register('privateScriptInspection', 'Private script inspection');
     Runtime.experiments.register('requestBlocking', 'Request blocking', true);
     Runtime.experiments.register('timelineShowAllEvents', 'Show all events on Timeline', true);
@@ -126,7 +127,7 @@
         Runtime.experiments.enableForTest('accessibilityInspection');
     }
 
-    Runtime.experiments.setDefaultExperiments(['timelineRecordingPerspectives']);
+    Runtime.experiments.setDefaultExperiments(['timelineRecordingPerspectives', 'persistenceValidation']);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/persistence/Persistence.js b/third_party/WebKit/Source/devtools/front_end/persistence/Persistence.js
index a67fd690..53c33e57 100644
--- a/third_party/WebKit/Source/devtools/front_end/persistence/Persistence.js
+++ b/third_party/WebKit/Source/devtools/front_end/persistence/Persistence.js
@@ -21,25 +21,62 @@
       var linkDecorator = new Persistence.PersistenceUtils.LinkDecorator(this);
       Components.Linkifier.setLinkDecorator(linkDecorator);
       this._mapping =
-          new Persistence.Automapping(workspace, this._onBindingCreated.bind(this), this._onBindingRemoved.bind(this));
+          new Persistence.Automapping(workspace, this._validateBinding.bind(this), this._onBindingRemoved.bind(this));
     } else {
       this._mapping = new Persistence.DefaultMapping(
-          workspace, fileSystemMapping, this._onBindingCreated.bind(this), this._onBindingRemoved.bind(this));
+          workspace, fileSystemMapping, this._validateBinding.bind(this), this._onBindingRemoved.bind(this));
     }
   }
 
   /**
    * @param {!Persistence.PersistenceBinding} binding
    */
-  _onBindingCreated(binding) {
-    if (binding.network.isDirty()) {
-      Common.console.log(
-          Common.UIString('%s can not be persisted to file system due to unsaved changes.', binding.network.name()));
+  _validateBinding(binding) {
+    if (!Runtime.experiments.isEnabled('persistenceValidation') || binding.network.contentType().isFromSourceMap() ||
+        !binding.fileSystem.contentType().isTextType()) {
+      this._establishBinding(binding);
       return;
     }
-    if (binding.fileSystem.isDirty())
-      binding.network.setWorkingCopy(binding.fileSystem.workingCopy());
 
+    Promise.all([binding.network.requestContent(), binding.fileSystem.requestContent()]).then(onContents.bind(this));
+
+    /**
+     * @this {Persistence.Persistence}
+     */
+    function onContents() {
+      if (binding._removed)
+        return;
+
+      var fileSystemContent = binding.fileSystem.workingCopy();
+      var networkContent = binding.network.workingCopy();
+      var target = Bindings.NetworkProject.targetForUISourceCode(binding.network);
+      var isValid = false;
+      if (target.isNodeJS()) {
+        var rewrappedNetworkContent = Persistence.Persistence._rewrapNodeJSContent(
+            binding, binding.fileSystem, fileSystemContent, networkContent);
+        isValid = (fileSystemContent === rewrappedNetworkContent);
+      } else {
+        // Trim trailing whitespaces because V8 adds trailing newline.
+        isValid = (fileSystemContent.trimRight() === networkContent.trimRight());
+      }
+
+      if (isValid)
+        this._establishBinding(binding);
+      else
+        this._prevalidationFailedForTest(binding);
+    }
+  }
+
+  /**
+   * @param {!Persistence.PersistenceBinding} binding
+   */
+  _prevalidationFailedForTest(binding) {
+  }
+
+  /**
+   * @param {!Persistence.PersistenceBinding} binding
+   */
+  _establishBinding(binding) {
     binding.network[Persistence.Persistence._binding] = binding;
     binding.fileSystem[Persistence.Persistence._binding] = binding;
 
@@ -64,6 +101,13 @@
    * @param {!Persistence.PersistenceBinding} binding
    */
   _onBindingRemoved(binding) {
+    binding._removed = true;
+    if (binding.network[Persistence.Persistence._binding] !== binding)
+      return;
+    console.assert(
+        binding.network[Persistence.Persistence._binding] === binding.fileSystem[Persistence.Persistence._binding],
+        'ERROR: inconsistent binding for networkURL ' + binding.network.url());
+
     binding.network[Persistence.Persistence._binding] = null;
     binding.fileSystem[Persistence.Persistence._binding] = null;
 
@@ -95,7 +139,8 @@
     if (target.isNodeJS()) {
       var newContent = uiSourceCode.workingCopy();
       other.requestContent().then(() => {
-        var nodeJSContent = this._rewrapNodeJSContent(binding, other, other.workingCopy(), newContent);
+        var nodeJSContent =
+            Persistence.Persistence._rewrapNodeJSContent(binding, other, other.workingCopy(), newContent);
         setWorkingCopy.call(this, () => nodeJSContent);
       });
       return;
@@ -128,7 +173,7 @@
     var target = Bindings.NetworkProject.targetForUISourceCode(binding.network);
     if (target.isNodeJS()) {
       other.requestContent().then(currentContent => {
-        var nodeJSContent = this._rewrapNodeJSContent(binding, other, currentContent, newContent);
+        var nodeJSContent = Persistence.Persistence._rewrapNodeJSContent(binding, other, currentContent, newContent);
         setContent.call(this, nodeJSContent);
       });
       return;
@@ -154,7 +199,7 @@
    * @param {string} newContent
    * @return {string}
    */
-  _rewrapNodeJSContent(binding, uiSourceCode, currentContent, newContent) {
+  static _rewrapNodeJSContent(binding, uiSourceCode, currentContent, newContent) {
     if (uiSourceCode === binding.fileSystem) {
       if (newContent.startsWith(Persistence.Persistence._NodePrefix) &&
           newContent.endsWith(Persistence.Persistence._NodeSuffix)) {
@@ -290,6 +335,7 @@
     this.network = network;
     this.fileSystem = fileSystem;
     this.exactMatch = exactMatch;
+    this._removed = false;
   }
 };
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index a5fee149..293dd99 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -74736,8 +74736,26 @@
   <summary>The type of category clicked in the Windows Jumplist</summary>
 </histogram>
 
+<histogram name="WinJumplist.DetailedFolderMoveResults"
+    enum="JumplistIconsDetailedFolderMoveCategory">
+  <owner>chengx@chromium.org</owner>
+  <summary>
+    This metric is recorded when folder JumpListIcons is moved (can be rename or
+    copy and delete) to JumpListIconsOld. This happens when tabs are closed,
+    mostly visited URLs get updated, etc. There are several key steps in this
+    folder move operation. Before the move operation, there is another step that
+    JumpListIconsOld folder is deleted. The status of these steps are put
+    together and recorded in this metric. The failure of any of these steps is
+    suspected to be related to a known issue.
+  </summary>
+</histogram>
+
 <histogram name="WinJumplist.FolderMoveResults"
     enum="JumplistIconsFolderMoveCategory">
+  <obsolete>
+    Obselete 12/08/2016. DetailedFolderMoveResults is used for more detailed
+    analysis.
+  </obsolete>
   <owner>chengx@chromium.org</owner>
   <summary>
     This metric is recorded when folder JumpListIcons is moved (can be rename or
@@ -91424,6 +91442,201 @@
   </int>
 </enum>
 
+<enum name="JumplistIconsDetailedFolderMoveCategory" type="int">
+  <int value="0"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="1"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="2"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="3"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="4"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="5"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="6"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="7"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="8"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="9"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="10"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="11"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="12"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Fail"/>
+  <int value="13"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Fail"/>
+  <int value="14"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Fail"/>
+  <int value="15"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Fail"/>
+  <int value="16"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="17"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="18"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="19"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="20"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="21"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="22"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="23"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="24"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="25"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="26"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="27"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="28"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Fail"/>
+  <int value="29"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Fail"/>
+  <int value="30"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Fail"/>
+  <int value="31"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Succeed - DelFile Fail - DelSourceDir Fail"/>
+  <int value="32"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="33"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="34"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="35"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="36"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="37"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="38"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="39"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="40"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="41"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="42"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="43"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="44"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Fail"/>
+  <int value="45"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Fail"/>
+  <int value="46"
+      label="DelTargetDir Succeed - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Fail"/>
+  <int value="47"
+      label="DelTargetDir Succeed - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Fail"/>
+  <int value="48"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="49"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="50"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="51"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Succeed"/>
+  <int value="52"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="53"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="54"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="55"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Succeed"/>
+  <int value="56"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="57"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="58"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="59"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Fail - DelFile Succeed - DelSourceDir Fail"/>
+  <int value="60"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Fail"/>
+  <int value="61"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Succeed -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Fail"/>
+  <int value="62"
+      label="DelTargetDir Fail - MoveFileEx Succeed - CopyFile Fail -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Fail"/>
+  <int value="63"
+      label="DelTargetDir Fail - MoveFileEx Fail - CopyFile Fail -
+             SourceDirRead Fail - DelFile Fail - DelSourceDir Fail"/>
+</enum>
+
 <enum name="JumplisticonsfolderCategory" type="int">
   <int value="0" label="Del Succeed - Mov Succeed - Create Succeed"/>
   <int value="1" label="Del Fail - Mov Succeed - Create Succeed"/>