diff --git a/DEPS b/DEPS
index d545b321..f8f432e 100644
--- a/DEPS
+++ b/DEPS
@@ -74,7 +74,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'e562f413ea3877842cbb8bc8711053ab06c9d9d9',
+  'skia_revision': 'be4ffab4e208ec47b4298621b9c9e8456f31717e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -98,7 +98,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '93bb725b62f9779534c9444c1e1319fe8c28912e',
+  'pdfium_revision': 'f8f22c7ac7f3d4b922f40f67e910114e55b187b0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -639,7 +639,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '05591bbeae6592fd924caec8e728a4ea86cbb8c9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '4d5b68e7bde264a5d164d94465f3818971fa6153', # commit position 20628
+    Var('webrtc_git') + '/src.git' + '@' + 'a1d0f2731001d26f50a977fab3f3419125f5cc43', # commit position 20628
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/ENG_REVIEW_OWNERS b/ENG_REVIEW_OWNERS
index d848886..50b15b8 100644
--- a/ENG_REVIEW_OWNERS
+++ b/ENG_REVIEW_OWNERS
@@ -4,7 +4,6 @@
 ben@chromium.org
 brettw@chromium.org
 darin@chromium.org
-dglazkov@chromium.org
 klobag@chromium.org
 jochen@chromium.org
 jam@chromium.org
diff --git a/WATCHLISTS b/WATCHLISTS
index cccf96f3..f8fcfb37b 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -513,15 +513,6 @@
       'filepath': 'third_party/WebKit/LayoutTests/external/' \
                   '|third_party/WebKit/Tools/Scripts/webkitpy/w3c/'
     },
-    'blink_webcomponents': {
-      'filepath': 'third_party/WebKit/Source/core/dom/.*Shadow' \
-                  '|third_party/WebKit/Source/core/dom/.*Slot' \
-                  '|third_party/WebKit/Source/core/dom/.*InsertionPoint' \
-                  '|third_party/WebKit/Source/core/dom/.*FlatTree' \
-                  '|third_party/WebKit/Source/core/html/custom/' \
-                  '|third_party/WebKit/Source/core/html/imports/' \
-                  '|third_party/WebKit/Source/bindings/core/v8/.*CustomElement'
-    },
     'blink_webp': {
       'filepath': 'third_party/WebKit/Source/platform/image-decoders/webp' \
                   '|third_party/WebKit/Source/platform/image-encoders/skia/WEBP'
@@ -1724,15 +1715,13 @@
     'blink_css': ['alexis.menard@intel.com',
                   'apavlov+blink@chromium.org',
                   'blink-reviews-css@chromium.org',
-                  'dglazkov+blink@chromium.org',
                   'rob.buis@samsung.com'],
     'blink_css_flexbox': ['cbiesinger@chromium.org'],
     'blink_css_fragmentation_tests': ['mstensho@chromium.org'],
     'blink_css_grid_layout': ['jfernandez@igalia.com',
                               'rego@igalia.com',
                               'svillar@igalia.com'],
-    'blink_custom_elements': ['dglazkov+blink@chromium.org',
-                              'dominicc+watchlist@chromium.org'],
+    'blink_custom_elements': ['dominicc+watchlist@chromium.org'],
     'blink_device_orientation': ['mlamouri+watch-blink@chromium.org',
                                  'timvolodine@chromium.org'],
     'blink_devtools': ['apavlov+blink@chromium.org',
@@ -1742,12 +1731,10 @@
                        'lushnikov+blink@chromium.org',
                        'pfeldman+blink@chromium.org'],
     'blink_dom': ['blink-reviews-dom@chromium.org',
-                  'dglazkov+blink@chromium.org',
                   'eae+blinkwatch@chromium.org',
                   'rob.buis@samsung.com'],
     'blink_dom_events': ['hayato@chromium.org'],
     'blink_events': ['blink-reviews-events@chromium.org',
-                     'dglazkov+blink@chromium.org',
                      'dtapuska+blinkwatch@chromium.org',
                      'eae+blinkwatch@chromium.org'],
     'blink_fetch': ['gavinp+loader@chromium.org',
@@ -1764,8 +1751,7 @@
                    'haraken@chromium.org',
                    'kouhei+heap@chromium.org',
                    'oilpan-reviews@chromium.org'],
-    'blink_html': ['blink-reviews-html@chromium.org',
-                   'dglazkov+blink@chromium.org'],
+    'blink_html': ['blink-reviews-html@chromium.org'],
     'blink_htmlparser': ['kinuko+watch@chromium.org',
                          'loading-reviews+parser@chromium.org'],
     'blink_indexed_db': ['cmumford@chromium.org',
@@ -1818,8 +1804,7 @@
     'blink_preloadScanner': ['yoav@yoav.ws'],
     'blink_prerender': ['gavinp+prerender@chromium.org',
                         'yoav@yoav.ws'],
-    'blink_public_api': ['blink-reviews-api@chromium.org',
-                         'dglazkov+blink@chromium.org'],
+    'blink_public_api': ['blink-reviews-api@chromium.org'],
     'blink_quota': ['kinuko+fileapi@chromium.org',
                     'nhiroki@chromium.org',
                     'tzik@chromium.org'],
@@ -1861,7 +1846,6 @@
     'blink_vibration': ['mlamouri+watch-blink@chromium.org'],
     'blink_viewport_interaction': ['kenneth.christiansen@gmail.com'],
     'blink_w3ctests': ['blink-reviews-w3ctests@chromium.org'],
-    'blink_webcomponents': ['dglazkov+blink@chromium.org'],
     'blink_webp': ['jzern@chromium.org',
                    'skal@google.com',
                    'urvang@chromium.org'],
diff --git a/ash/message_center/notifier_settings_view.h b/ash/message_center/notifier_settings_view.h
index ccb9c3a..b69bafb 100644
--- a/ash/message_center/notifier_settings_view.h
+++ b/ash/message_center/notifier_settings_view.h
@@ -97,9 +97,6 @@
   // Overridden from views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
-  // Callback for views::MenuModelAdapter.
-  void OnMenuClosed();
-
   views::ImageButton* title_arrow_;
   views::ImageView* quiet_mode_icon_;
   views::ToggleButton* quiet_mode_toggle_;
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index ddc51f54..bd1b553 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -641,17 +641,14 @@
   menu_model_ = std::make_unique<ShelfContextMenuModel>(
       std::vector<mojom::MenuItemPtr>(), nullptr, display_id);
 
-  menu_model_adapter_ = std::make_unique<views::MenuModelAdapter>(
-      menu_model_.get(),
-      base::Bind(&RootWindowController::OnMenuClosed, base::Unretained(this)));
-
   // The wallpaper controller may not be set yet if the user clicked on the
   // status area before the initial animation completion. See crbug.com/222218
   if (!wallpaper_widget_controller())
     return;
 
   menu_runner_ = std::make_unique<views::MenuRunner>(
-      menu_model_adapter_->CreateMenu(), views::MenuRunner::CONTEXT_MENU);
+      menu_model_.get(), views::MenuRunner::CONTEXT_MENU,
+      base::Bind(&RootWindowController::OnMenuClosed, base::Unretained(this)));
   menu_runner_->RunMenuAt(wallpaper_widget_controller()->widget(), nullptr,
                           gfx::Rect(location_in_screen, gfx::Size()),
                           views::MENU_ANCHOR_TOPLEFT, source_type);
@@ -1029,7 +1026,6 @@
 
 void RootWindowController::OnMenuClosed() {
   menu_runner_.reset();
-  menu_model_adapter_.reset();
   menu_model_.reset();
   shelf_->UpdateVisibilityState();
 }
diff --git a/ash/root_window_controller.h b/ash/root_window_controller.h
index 5b289ed..ffff227b 100644
--- a/ash/root_window_controller.h
+++ b/ash/root_window_controller.h
@@ -36,7 +36,6 @@
 }
 
 namespace views {
-class MenuModelAdapter;
 class MenuRunner;
 class Widget;
 }
@@ -283,7 +282,7 @@
   // this.
   void ResetRootForNewWindowsIfNecessary();
 
-  // Callback for MenuModelAdapter.
+  // Callback for MenuRunner.
   void OnMenuClosed();
 
   std::unique_ptr<AshWindowTreeHost> ash_host_;
@@ -304,7 +303,6 @@
 
   // Manages the context menu.
   std::unique_ptr<ui::MenuModel> menu_model_;
-  std::unique_ptr<views::MenuModelAdapter> menu_model_adapter_;
   std::unique_ptr<views::MenuRunner> menu_runner_;
 
   std::unique_ptr<StackingController> stacking_controller_;
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index ea92f29c..0a4b5ae 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -1793,9 +1793,6 @@
                          ui::MenuSourceType source_type,
                          views::InkDrop* ink_drop) {
   menu_model_ = std::move(menu_model);
-  menu_model_adapter_.reset(new views::MenuModelAdapter(
-      menu_model_.get(),
-      base::Bind(&ShelfView::OnMenuClosed, base::Unretained(this), ink_drop)));
 
   closing_event_time_ = base::TimeTicks();
   int run_types = 0;
@@ -1810,8 +1807,9 @@
     run_types |= views::MenuRunner::SEND_GESTURE_EVENTS_TO_OWNER;
   }
 
-  launcher_menu_runner_.reset(
-      new views::MenuRunner(menu_model_adapter_->CreateMenu(), run_types));
+  launcher_menu_runner_ = std::make_unique<views::MenuRunner>(
+      menu_model_.get(), run_types,
+      base::Bind(&ShelfView::OnMenuClosed, base::Unretained(this), ink_drop));
 
   views::MenuAnchorPosition menu_alignment = views::MENU_ANCHOR_TOPLEFT;
   gfx::Rect anchor = gfx::Rect(click_point, gfx::Size());
@@ -1865,7 +1863,6 @@
     ink_drop->AnimateToState(views::InkDropState::DEACTIVATED);
 
   launcher_menu_runner_.reset();
-  menu_model_adapter_.reset();
   menu_model_.reset();
 
   // Auto-hide or alignment might have changed, but only for this shelf.
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 26d0f1b4..36ec275 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -34,7 +34,6 @@
 
 namespace views {
 class BoundsAnimator;
-class MenuModelAdapter;
 class MenuRunner;
 }
 
@@ -363,7 +362,7 @@
                 ui::MenuSourceType source_type,
                 views::InkDrop* ink_drop);
 
-  // Callback for MenuModelAdapter.
+  // Callback for MenuRunner.
   void OnMenuClosed(views::InkDrop* ink_drop);
 
   // Overridden from views::BoundsAnimatorObserver:
@@ -436,7 +435,6 @@
 
   // Manages the context menu, and the list menu.
   std::unique_ptr<ui::MenuModel> menu_model_;
-  std::unique_ptr<views::MenuModelAdapter> menu_model_adapter_;
   std::unique_ptr<views::MenuRunner> launcher_menu_runner_;
   std::unique_ptr<ScopedRootWindowForNewWindows>
       scoped_root_window_for_new_windows_;
diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc
index dd6382a..eaebceb1 100644
--- a/ash/system/bluetooth/tray_bluetooth.cc
+++ b/ash/system/bluetooth/tray_bluetooth.cc
@@ -201,6 +201,14 @@
   }
 
   void Update() {
+    // Update immediately for initial device list and
+    // when bluetooth is disabled.
+    if (device_map_.size() == 0 ||
+        !Shell::Get()->tray_bluetooth_helper()->GetBluetoothEnabled()) {
+      DoUpdate();
+      return;
+    }
+
     // Return here since an update is already queued.
     if (timer_.IsRunning())
       return;
diff --git a/base/i18n/timezone.cc b/base/i18n/timezone.cc
index 95e7aee3..8624e07e 100644
--- a/base/i18n/timezone.cc
+++ b/base/i18n/timezone.cc
@@ -4,615 +4,31 @@
 
 #include "base/i18n/timezone.h"
 
-#include <stddef.h>
-#include <string.h>
+#include <memory>
+#include <string>
 
-#include <map>
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "base/strings/string16.h"
-#include "base/strings/utf_string_conversions.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
 #include "third_party/icu/source/i18n/unicode/timezone.h"
 
 namespace base {
 
-namespace {
-
-class TimezoneMap {
- public:
-  static TimezoneMap* GetInstance() {
-    return Singleton<TimezoneMap>::get();
-  }
-
-  std::string CountryCodeForTimezone(const std::string& olson_code) {
-    std::map<const char*, const char*, CompareCStrings>::iterator iter =
-      map_.find(olson_code.c_str());
-    if (iter != map_.end())
-      return iter->second;
-
-    return std::string();
-  }
-
- private:
-  TimezoneMap() {
-    // These mappings are adapted from zone.tab, which is available at
-    // <http://www.ietf.org/timezones/data/zone.tab> and is a part of public
-    // domain.
-    struct OlsonCodeData {
-      const char* country_code;
-      const char* olson_code;
-    };
-    static const OlsonCodeData olson_code_data[] = {
-        { "AD", "Europe/Andorra" },
-        { "AE", "Asia/Dubai" },
-        { "AF", "Asia/Kabul" },
-        { "AG", "America/Antigua" },
-        { "AI", "America/Anguilla" },
-        { "AL", "Europe/Tirane" },
-        { "AM", "Asia/Yerevan" },
-        { "AO", "Africa/Luanda" },
-        { "AQ", "Antarctica/McMurdo" },
-        { "AQ", "Antarctica/Rothera" },
-        { "AQ", "Antarctica/Palmer" },
-        { "AQ", "Antarctica/Mawson" },
-        { "AQ", "Antarctica/Davis" },
-        { "AQ", "Antarctica/Casey" },
-        { "AQ", "Antarctica/Vostok" },
-        { "AQ", "Antarctica/DumontDUrville" },
-        { "AQ", "Antarctica/Syowa" },
-        { "AR", "America/Argentina/Buenos_Aires" },
-        { "AR", "America/Argentina/Cordoba" },
-        { "AR", "America/Argentina/Salta" },
-        { "AR", "America/Argentina/Jujuy" },
-        { "AR", "America/Argentina/Tucuman" },
-        { "AR", "America/Argentina/Catamarca" },
-        { "AR", "America/Argentina/La_Rioja" },
-        { "AR", "America/Argentina/San_Juan" },
-        { "AR", "America/Argentina/Mendoza" },
-        { "AR", "America/Argentina/San_Luis" },
-        { "AR", "America/Argentina/Rio_Gallegos" },
-        { "AR", "America/Argentina/Ushuaia" },
-        { "AS", "Pacific/Pago_Pago" },
-        { "AT", "Europe/Vienna" },
-        { "AU", "Australia/Lord_Howe" },
-        { "AU", "Antarctica/Macquarie" },
-        { "AU", "Australia/Hobart" },
-        { "AU", "Australia/Currie" },
-        { "AU", "Australia/Melbourne" },
-        { "AU", "Australia/Sydney" },
-        { "AU", "Australia/Broken_Hill" },
-        { "AU", "Australia/Brisbane" },
-        { "AU", "Australia/Lindeman" },
-        { "AU", "Australia/Adelaide" },
-        { "AU", "Australia/Darwin" },
-        { "AU", "Australia/Perth" },
-        { "AU", "Australia/Eucla" },
-        { "AW", "America/Aruba" },
-        { "AX", "Europe/Mariehamn" },
-        { "AZ", "Asia/Baku" },
-        { "BA", "Europe/Sarajevo" },
-        { "BB", "America/Barbados" },
-        { "BD", "Asia/Dhaka" },
-        { "BE", "Europe/Brussels" },
-        { "BF", "Africa/Ouagadougou" },
-        { "BG", "Europe/Sofia" },
-        { "BH", "Asia/Bahrain" },
-        { "BI", "Africa/Bujumbura" },
-        { "BJ", "Africa/Porto-Novo" },
-        { "BL", "America/St_Barthelemy" },
-        { "BM", "Atlantic/Bermuda" },
-        { "BN", "Asia/Brunei" },
-        { "BO", "America/La_Paz" },
-        { "BQ", "America/Kralendijk" },
-        { "BR", "America/Noronha" },
-        { "BR", "America/Belem" },
-        { "BR", "America/Fortaleza" },
-        { "BR", "America/Recife" },
-        { "BR", "America/Araguaina" },
-        { "BR", "America/Maceio" },
-        { "BR", "America/Bahia" },
-        { "BR", "America/Sao_Paulo" },
-        { "BR", "America/Campo_Grande" },
-        { "BR", "America/Cuiaba" },
-        { "BR", "America/Santarem" },
-        { "BR", "America/Porto_Velho" },
-        { "BR", "America/Boa_Vista" },
-        { "BR", "America/Manaus" },
-        { "BR", "America/Eirunepe" },
-        { "BR", "America/Rio_Branco" },
-        { "BS", "America/Nassau" },
-        { "BT", "Asia/Thimphu" },
-        { "BW", "Africa/Gaborone" },
-        { "BY", "Europe/Minsk" },
-        { "BZ", "America/Belize" },
-        { "CA", "America/St_Johns" },
-        { "CA", "America/Halifax" },
-        { "CA", "America/Glace_Bay" },
-        { "CA", "America/Moncton" },
-        { "CA", "America/Goose_Bay" },
-        { "CA", "America/Blanc-Sablon" },
-        { "CA", "America/Toronto" },
-        { "CA", "America/Nipigon" },
-        { "CA", "America/Thunder_Bay" },
-        { "CA", "America/Iqaluit" },
-        { "CA", "America/Pangnirtung" },
-        { "CA", "America/Resolute" },
-        { "CA", "America/Atikokan" },
-        { "CA", "America/Rankin_Inlet" },
-        { "CA", "America/Winnipeg" },
-        { "CA", "America/Rainy_River" },
-        { "CA", "America/Regina" },
-        { "CA", "America/Swift_Current" },
-        { "CA", "America/Edmonton" },
-        { "CA", "America/Cambridge_Bay" },
-        { "CA", "America/Yellowknife" },
-        { "CA", "America/Inuvik" },
-        { "CA", "America/Creston" },
-        { "CA", "America/Dawson_Creek" },
-        { "CA", "America/Vancouver" },
-        { "CA", "America/Whitehorse" },
-        { "CA", "America/Dawson" },
-        { "CC", "Indian/Cocos" },
-        { "CD", "Africa/Kinshasa" },
-        { "CD", "Africa/Lubumbashi" },
-        { "CF", "Africa/Bangui" },
-        { "CG", "Africa/Brazzaville" },
-        { "CH", "Europe/Zurich" },
-        { "CI", "Africa/Abidjan" },
-        { "CK", "Pacific/Rarotonga" },
-        { "CL", "America/Santiago" },
-        { "CL", "Pacific/Easter" },
-        { "CM", "Africa/Douala" },
-        { "CN", "Asia/Shanghai" },
-        { "CN", "Asia/Harbin" },
-        { "CN", "Asia/Chongqing" },
-        { "CN", "Asia/Urumqi" },
-        { "CN", "Asia/Kashgar" },
-        { "CO", "America/Bogota" },
-        { "CR", "America/Costa_Rica" },
-        { "CU", "America/Havana" },
-        { "CV", "Atlantic/Cape_Verde" },
-        { "CW", "America/Curacao" },
-        { "CX", "Indian/Christmas" },
-        { "CY", "Asia/Nicosia" },
-        { "CZ", "Europe/Prague" },
-        { "DE", "Europe/Berlin" },
-        { "DE", "Europe/Busingen" },
-        { "DJ", "Africa/Djibouti" },
-        { "DK", "Europe/Copenhagen" },
-        { "DM", "America/Dominica" },
-        { "DO", "America/Santo_Domingo" },
-        { "DZ", "Africa/Algiers" },
-        { "EC", "America/Guayaquil" },
-        { "EC", "Pacific/Galapagos" },
-        { "EE", "Europe/Tallinn" },
-        { "EG", "Africa/Cairo" },
-        { "EH", "Africa/El_Aaiun" },
-        { "ER", "Africa/Asmara" },
-        { "ES", "Europe/Madrid" },
-        { "ES", "Africa/Ceuta" },
-        { "ES", "Atlantic/Canary" },
-        { "ET", "Africa/Addis_Ababa" },
-        { "FI", "Europe/Helsinki" },
-        { "FJ", "Pacific/Fiji" },
-        { "FK", "Atlantic/Stanley" },
-        { "FM", "Pacific/Chuuk" },
-        { "FM", "Pacific/Pohnpei" },
-        { "FM", "Pacific/Kosrae" },
-        { "FO", "Atlantic/Faroe" },
-        { "FR", "Europe/Paris" },
-        { "GA", "Africa/Libreville" },
-        { "GB", "Europe/London" },
-        { "GD", "America/Grenada" },
-        { "GE", "Asia/Tbilisi" },
-        { "GF", "America/Cayenne" },
-        { "GG", "Europe/Guernsey" },
-        { "GH", "Africa/Accra" },
-        { "GI", "Europe/Gibraltar" },
-        { "GL", "America/Godthab" },
-        { "GL", "America/Danmarkshavn" },
-        { "GL", "America/Scoresbysund" },
-        { "GL", "America/Thule" },
-        { "GM", "Africa/Banjul" },
-        { "GN", "Africa/Conakry" },
-        { "GP", "America/Guadeloupe" },
-        { "GQ", "Africa/Malabo" },
-        { "GR", "Europe/Athens" },
-        { "GS", "Atlantic/South_Georgia" },
-        { "GT", "America/Guatemala" },
-        { "GU", "Pacific/Guam" },
-        { "GW", "Africa/Bissau" },
-        { "GY", "America/Guyana" },
-        { "HK", "Asia/Hong_Kong" },
-        { "HN", "America/Tegucigalpa" },
-        { "HR", "Europe/Zagreb" },
-        { "HT", "America/Port-au-Prince" },
-        { "HU", "Europe/Budapest" },
-        { "ID", "Asia/Jakarta" },
-        { "ID", "Asia/Pontianak" },
-        { "ID", "Asia/Makassar" },
-        { "ID", "Asia/Jayapura" },
-        { "IE", "Europe/Dublin" },
-        { "IL", "Asia/Jerusalem" },
-        { "IM", "Europe/Isle_of_Man" },
-        { "IN", "Asia/Kolkata" },
-        { "IO", "Indian/Chagos" },
-        { "IQ", "Asia/Baghdad" },
-        { "IR", "Asia/Tehran" },
-        { "IS", "Atlantic/Reykjavik" },
-        { "IT", "Europe/Rome" },
-        { "JE", "Europe/Jersey" },
-        { "JM", "America/Jamaica" },
-        { "JO", "Asia/Amman" },
-        { "JP", "Asia/Tokyo" },
-        { "KE", "Africa/Nairobi" },
-        { "KG", "Asia/Bishkek" },
-        { "KH", "Asia/Phnom_Penh" },
-        { "KI", "Pacific/Tarawa" },
-        { "KI", "Pacific/Enderbury" },
-        { "KI", "Pacific/Kiritimati" },
-        { "KM", "Indian/Comoro" },
-        { "KN", "America/St_Kitts" },
-        { "KP", "Asia/Pyongyang" },
-        { "KR", "Asia/Seoul" },
-        { "KW", "Asia/Kuwait" },
-        { "KY", "America/Cayman" },
-        { "KZ", "Asia/Almaty" },
-        { "KZ", "Asia/Qyzylorda" },
-        { "KZ", "Asia/Aqtobe" },
-        { "KZ", "Asia/Aqtau" },
-        { "KZ", "Asia/Oral" },
-        { "LA", "Asia/Vientiane" },
-        { "LB", "Asia/Beirut" },
-        { "LC", "America/St_Lucia" },
-        { "LI", "Europe/Vaduz" },
-        { "LK", "Asia/Colombo" },
-        { "LR", "Africa/Monrovia" },
-        { "LS", "Africa/Maseru" },
-        { "LT", "Europe/Vilnius" },
-        { "LU", "Europe/Luxembourg" },
-        { "LV", "Europe/Riga" },
-        { "LY", "Africa/Tripoli" },
-        { "MA", "Africa/Casablanca" },
-        { "MC", "Europe/Monaco" },
-        { "MD", "Europe/Chisinau" },
-        { "ME", "Europe/Podgorica" },
-        { "MF", "America/Marigot" },
-        { "MG", "Indian/Antananarivo" },
-        { "MH", "Pacific/Majuro" },
-        { "MH", "Pacific/Kwajalein" },
-        { "MK", "Europe/Skopje" },
-        { "ML", "Africa/Bamako" },
-        { "MM", "Asia/Rangoon" },
-        { "MN", "Asia/Ulaanbaatar" },
-        { "MN", "Asia/Hovd" },
-        { "MN", "Asia/Choibalsan" },
-        { "MO", "Asia/Macau" },
-        { "MP", "Pacific/Saipan" },
-        { "MQ", "America/Martinique" },
-        { "MR", "Africa/Nouakchott" },
-        { "MS", "America/Montserrat" },
-        { "MT", "Europe/Malta" },
-        { "MU", "Indian/Mauritius" },
-        { "MV", "Indian/Maldives" },
-        { "MW", "Africa/Blantyre" },
-        { "MX", "America/Mexico_City" },
-        { "MX", "America/Cancun" },
-        { "MX", "America/Merida" },
-        { "MX", "America/Monterrey" },
-        { "MX", "America/Matamoros" },
-        { "MX", "America/Mazatlan" },
-        { "MX", "America/Chihuahua" },
-        { "MX", "America/Ojinaga" },
-        { "MX", "America/Hermosillo" },
-        { "MX", "America/Tijuana" },
-        { "MX", "America/Santa_Isabel" },
-        { "MX", "America/Bahia_Banderas" },
-        { "MY", "Asia/Kuala_Lumpur" },
-        { "MY", "Asia/Kuching" },
-        { "MZ", "Africa/Maputo" },
-        { "NA", "Africa/Windhoek" },
-        { "NC", "Pacific/Noumea" },
-        { "NE", "Africa/Niamey" },
-        { "NF", "Pacific/Norfolk" },
-        { "NG", "Africa/Lagos" },
-        { "NI", "America/Managua" },
-        { "NL", "Europe/Amsterdam" },
-        { "NO", "Europe/Oslo" },
-        { "NP", "Asia/Kathmandu" },
-        { "NR", "Pacific/Nauru" },
-        { "NU", "Pacific/Niue" },
-        { "NZ", "Pacific/Auckland" },
-        { "NZ", "Pacific/Chatham" },
-        { "OM", "Asia/Muscat" },
-        { "PA", "America/Panama" },
-        { "PE", "America/Lima" },
-        { "PF", "Pacific/Tahiti" },
-        { "PF", "Pacific/Marquesas" },
-        { "PF", "Pacific/Gambier" },
-        { "PG", "Pacific/Port_Moresby" },
-        { "PH", "Asia/Manila" },
-        { "PK", "Asia/Karachi" },
-        { "PL", "Europe/Warsaw" },
-        { "PM", "America/Miquelon" },
-        { "PN", "Pacific/Pitcairn" },
-        { "PR", "America/Puerto_Rico" },
-        { "PS", "Asia/Gaza" },
-        { "PS", "Asia/Hebron" },
-        { "PT", "Europe/Lisbon" },
-        { "PT", "Atlantic/Madeira" },
-        { "PT", "Atlantic/Azores" },
-        { "PW", "Pacific/Palau" },
-        { "PY", "America/Asuncion" },
-        { "QA", "Asia/Qatar" },
-        { "RE", "Indian/Reunion" },
-        { "RO", "Europe/Bucharest" },
-        { "RS", "Europe/Belgrade" },
-        { "RU", "Europe/Kaliningrad" },
-        { "RU", "Europe/Moscow" },
-        { "RU", "Europe/Volgograd" },
-        { "RU", "Europe/Samara" },
-        { "RU", "Asia/Yekaterinburg" },
-        { "RU", "Asia/Omsk" },
-        { "RU", "Asia/Novosibirsk" },
-        { "RU", "Asia/Novokuznetsk" },
-        { "RU", "Asia/Krasnoyarsk" },
-        { "RU", "Asia/Irkutsk" },
-        { "RU", "Asia/Yakutsk" },
-        { "RU", "Asia/Khandyga" },
-        { "RU", "Asia/Vladivostok" },
-        { "RU", "Asia/Sakhalin" },
-        { "RU", "Asia/Ust-Nera" },
-        { "RU", "Asia/Magadan" },
-        { "RU", "Asia/Kamchatka" },
-        { "RU", "Asia/Anadyr" },
-        { "RW", "Africa/Kigali" },
-        { "SA", "Asia/Riyadh" },
-        { "SB", "Pacific/Guadalcanal" },
-        { "SC", "Indian/Mahe" },
-        { "SD", "Africa/Khartoum" },
-        { "SE", "Europe/Stockholm" },
-        { "SG", "Asia/Singapore" },
-        { "SH", "Atlantic/St_Helena" },
-        { "SI", "Europe/Ljubljana" },
-        { "SJ", "Arctic/Longyearbyen" },
-        { "SK", "Europe/Bratislava" },
-        { "SL", "Africa/Freetown" },
-        { "SM", "Europe/San_Marino" },
-        { "SN", "Africa/Dakar" },
-        { "SO", "Africa/Mogadishu" },
-        { "SR", "America/Paramaribo" },
-        { "SS", "Africa/Juba" },
-        { "ST", "Africa/Sao_Tome" },
-        { "SV", "America/El_Salvador" },
-        { "SX", "America/Lower_Princes" },
-        { "SY", "Asia/Damascus" },
-        { "SZ", "Africa/Mbabane" },
-        { "TC", "America/Grand_Turk" },
-        { "TD", "Africa/Ndjamena" },
-        { "TF", "Indian/Kerguelen" },
-        { "TG", "Africa/Lome" },
-        { "TH", "Asia/Bangkok" },
-        { "TJ", "Asia/Dushanbe" },
-        { "TK", "Pacific/Fakaofo" },
-        { "TL", "Asia/Dili" },
-        { "TM", "Asia/Ashgabat" },
-        { "TN", "Africa/Tunis" },
-        { "TO", "Pacific/Tongatapu" },
-        { "TR", "Europe/Istanbul" },
-        { "TT", "America/Port_of_Spain" },
-        { "TV", "Pacific/Funafuti" },
-        { "TW", "Asia/Taipei" },
-        { "TZ", "Africa/Dar_es_Salaam" },
-        { "UA", "Europe/Kiev" },
-        { "UA", "Europe/Uzhgorod" },
-        { "UA", "Europe/Zaporozhye" },
-        { "UA", "Europe/Simferopol" },
-        { "UG", "Africa/Kampala" },
-        { "UM", "Pacific/Johnston" },
-        { "UM", "Pacific/Midway" },
-        { "UM", "Pacific/Wake" },
-        { "US", "America/New_York" },
-        { "US", "America/Detroit" },
-        { "US", "America/Kentucky/Louisville" },
-        { "US", "America/Kentucky/Monticello" },
-        { "US", "America/Indiana/Indianapolis" },
-        { "US", "America/Indiana/Vincennes" },
-        { "US", "America/Indiana/Winamac" },
-        { "US", "America/Indiana/Marengo" },
-        { "US", "America/Indiana/Petersburg" },
-        { "US", "America/Indiana/Vevay" },
-        { "US", "America/Chicago" },
-        { "US", "America/Indiana/Tell_City" },
-        { "US", "America/Indiana/Knox" },
-        { "US", "America/Menominee" },
-        { "US", "America/North_Dakota/Center" },
-        { "US", "America/North_Dakota/New_Salem" },
-        { "US", "America/North_Dakota/Beulah" },
-        { "US", "America/Denver" },
-        { "US", "America/Boise" },
-        { "US", "America/Phoenix" },
-        { "US", "America/Los_Angeles" },
-        { "US", "America/Anchorage" },
-        { "US", "America/Juneau" },
-        { "US", "America/Sitka" },
-        { "US", "America/Yakutat" },
-        { "US", "America/Nome" },
-        { "US", "America/Adak" },
-        { "US", "America/Metlakatla" },
-        { "US", "Pacific/Honolulu" },
-        { "UY", "America/Montevideo" },
-        { "UZ", "Asia/Samarkand" },
-        { "UZ", "Asia/Tashkent" },
-        { "VA", "Europe/Vatican" },
-        { "VC", "America/St_Vincent" },
-        { "VE", "America/Caracas" },
-        { "VG", "America/Tortola" },
-        { "VI", "America/St_Thomas" },
-        { "VN", "Asia/Ho_Chi_Minh" },
-        { "VU", "Pacific/Efate" },
-        { "WF", "Pacific/Wallis" },
-        { "WS", "Pacific/Apia" },
-        { "YE", "Asia/Aden" },
-        { "YT", "Indian/Mayotte" },
-        { "ZA", "Africa/Johannesburg" },
-        { "ZM", "Africa/Lusaka" },
-        { "ZW", "Africa/Harare" },
-        // The mappings below are custom additions to zone.tab.
-        { "GB", "Etc/GMT" },
-        { "GB", "Etc/UTC" },
-        { "GB", "Etc/UCT" },
-    };
-
-    for (size_t i = 0; i < arraysize(olson_code_data); ++i)
-      map_[olson_code_data[i].olson_code] = olson_code_data[i].country_code;
-
-    // These are mapping from old codenames to new codenames. They are also
-    // part of public domain, and available at
-    // <http://www.ietf.org/timezones/data/backward>.
-    struct LinkData {
-      const char* old_code;
-      const char* new_code;
-    };
-    static const LinkData link_data[] = {
-        { "Africa/Asmera", "Africa/Asmara" },
-        { "Africa/Timbuktu", "Africa/Bamako" },
-        { "America/Argentina/ComodRivadavia", "America/Argentina/Catamarca" },
-        { "America/Atka", "America/Adak" },
-        { "America/Buenos_Aires", "America/Argentina/Buenos_Aires" },
-        { "America/Catamarca", "America/Argentina/Catamarca" },
-        { "America/Coral_Harbour", "America/Atikokan" },
-        { "America/Cordoba", "America/Argentina/Cordoba" },
-        { "America/Ensenada", "America/Tijuana" },
-        { "America/Fort_Wayne", "America/Indiana/Indianapolis" },
-        { "America/Indianapolis", "America/Indiana/Indianapolis" },
-        { "America/Jujuy", "America/Argentina/Jujuy" },
-        { "America/Knox_IN", "America/Indiana/Knox" },
-        { "America/Louisville", "America/Kentucky/Louisville" },
-        { "America/Mendoza", "America/Argentina/Mendoza" },
-        { "America/Porto_Acre", "America/Rio_Branco" },
-        { "America/Rosario", "America/Argentina/Cordoba" },
-        { "America/Virgin", "America/St_Thomas" },
-        { "Asia/Ashkhabad", "Asia/Ashgabat" },
-        { "Asia/Chungking", "Asia/Chongqing" },
-        { "Asia/Dacca", "Asia/Dhaka" },
-        { "Asia/Katmandu", "Asia/Kathmandu" },
-        { "Asia/Calcutta", "Asia/Kolkata" },
-        { "Asia/Macao", "Asia/Macau" },
-        { "Asia/Tel_Aviv", "Asia/Jerusalem" },
-        { "Asia/Saigon", "Asia/Ho_Chi_Minh" },
-        { "Asia/Thimbu", "Asia/Thimphu" },
-        { "Asia/Ujung_Pandang", "Asia/Makassar" },
-        { "Asia/Ulan_Bator", "Asia/Ulaanbaatar" },
-        { "Atlantic/Faeroe", "Atlantic/Faroe" },
-        { "Atlantic/Jan_Mayen", "Europe/Oslo" },
-        { "Australia/ACT", "Australia/Sydney" },
-        { "Australia/Canberra", "Australia/Sydney" },
-        { "Australia/LHI", "Australia/Lord_Howe" },
-        { "Australia/NSW", "Australia/Sydney" },
-        { "Australia/North", "Australia/Darwin" },
-        { "Australia/Queensland", "Australia/Brisbane" },
-        { "Australia/South", "Australia/Adelaide" },
-        { "Australia/Tasmania", "Australia/Hobart" },
-        { "Australia/Victoria", "Australia/Melbourne" },
-        { "Australia/West", "Australia/Perth" },
-        { "Australia/Yancowinna", "Australia/Broken_Hill" },
-        { "Brazil/Acre", "America/Rio_Branco" },
-        { "Brazil/DeNoronha", "America/Noronha" },
-        { "Brazil/East", "America/Sao_Paulo" },
-        { "Brazil/West", "America/Manaus" },
-        { "Canada/Atlantic", "America/Halifax" },
-        { "Canada/Central", "America/Winnipeg" },
-        { "Canada/East-Saskatchewan", "America/Regina" },
-        { "Canada/Eastern", "America/Toronto" },
-        { "Canada/Mountain", "America/Edmonton" },
-        { "Canada/Newfoundland", "America/St_Johns" },
-        { "Canada/Pacific", "America/Vancouver" },
-        { "Canada/Saskatchewan", "America/Regina" },
-        { "Canada/Yukon", "America/Whitehorse" },
-        { "Chile/Continental", "America/Santiago" },
-        { "Chile/EasterIsland", "Pacific/Easter" },
-        { "Cuba", "America/Havana" },
-        { "Egypt", "Africa/Cairo" },
-        { "Eire", "Europe/Dublin" },
-        { "Europe/Belfast", "Europe/London" },
-        { "Europe/Tiraspol", "Europe/Chisinau" },
-        { "GB", "Europe/London" },
-        { "GB-Eire", "Europe/London" },
-        { "GMT+0", "Etc/GMT" },
-        { "GMT-0", "Etc/GMT" },
-        { "GMT0", "Etc/GMT" },
-        { "Greenwich", "Etc/GMT" },
-        { "Hongkong", "Asia/Hong_Kong" },
-        { "Iceland", "Atlantic/Reykjavik" },
-        { "Iran", "Asia/Tehran" },
-        { "Israel", "Asia/Jerusalem" },
-        { "Jamaica", "America/Jamaica" },
-        { "Japan", "Asia/Tokyo" },
-        { "Kwajalein", "Pacific/Kwajalein" },
-        { "Libya", "Africa/Tripoli" },
-        { "Mexico/BajaNorte", "America/Tijuana" },
-        { "Mexico/BajaSur", "America/Mazatlan" },
-        { "Mexico/General", "America/Mexico_City" },
-        { "NZ", "Pacific/Auckland" },
-        { "NZ-CHAT", "Pacific/Chatham" },
-        { "Navajo", "America/Denver" },
-        { "PRC", "Asia/Shanghai" },
-        { "Pacific/Samoa", "Pacific/Pago_Pago" },
-        { "Pacific/Yap", "Pacific/Chuuk" },
-        { "Pacific/Truk", "Pacific/Chuuk" },
-        { "Pacific/Ponape", "Pacific/Pohnpei" },
-        { "Poland", "Europe/Warsaw" },
-        { "Portugal", "Europe/Lisbon" },
-        { "ROC", "Asia/Taipei" },
-        { "ROK", "Asia/Seoul" },
-        { "Singapore", "Asia/Singapore" },
-        { "Turkey", "Europe/Istanbul" },
-        { "UCT", "Etc/UCT" },
-        { "US/Alaska", "America/Anchorage" },
-        { "US/Aleutian", "America/Adak" },
-        { "US/Arizona", "America/Phoenix" },
-        { "US/Central", "America/Chicago" },
-        { "US/East-Indiana", "America/Indiana/Indianapolis" },
-        { "US/Eastern", "America/New_York" },
-        { "US/Hawaii", "Pacific/Honolulu" },
-        { "US/Indiana-Starke", "America/Indiana/Knox" },
-        { "US/Michigan", "America/Detroit" },
-        { "US/Mountain", "America/Denver" },
-        { "US/Pacific", "America/Los_Angeles" },
-        { "US/Samoa", "Pacific/Pago_Pago" },
-        { "UTC", "Etc/UTC" },
-        { "Universal", "Etc/UTC" },
-        { "W-SU", "Europe/Moscow" },
-        { "Zulu", "Etc/UTC" },
-    };
-
-    for (size_t i = 0; i < arraysize(link_data); ++i)
-      map_[link_data[i].old_code] = map_[link_data[i].new_code];
-  }
-
-  friend struct DefaultSingletonTraits<TimezoneMap>;
-
-  struct CompareCStrings {
-    bool operator()(const char* str1, const char* str2) const {
-      return strcmp(str1, str2) < 0;
-    }
-  };
-  std::map<const char*, const char*, CompareCStrings> map_;
-
-  DISALLOW_COPY_AND_ASSIGN(TimezoneMap);
-};
-
-}  // namespace
-
 std::string CountryCodeForCurrentTimezone() {
   std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
   icu::UnicodeString id;
-  zone->getID(id);
-  std::string olson_code;
-  return TimezoneMap::GetInstance()->CountryCodeForTimezone(
-      id.toUTF8String(olson_code));
+  // ICU returns '001' (world) for Etc/GMT. Preserve the old behavior
+  // only for Etc/GMT while returning an empty string for Etc/UTC and
+  // Etc/UCT because they're less likely to be chosen by mistake in UK in
+  // place of Europe/London (Briitish Time).
+  if (zone->getID(id) == UNICODE_STRING_SIMPLE("Etc/GMT"))
+    return "GB";
+  char region_code[4];
+  UErrorCode status = U_ZERO_ERROR;
+  int length = zone->getRegion(id, region_code, 4, status);
+  // Return an empty string if region_code is a 3-digit numeric code such
+  // as 001 (World) for Etc/UTC, Etc/UCT.
+  return (U_SUCCESS(status) && length == 2)
+             ? std::string(region_code, static_cast<size_t>(length))
+             : std::string();
 }
 
 }  // namespace base
diff --git a/base/i18n/timezone.h b/base/i18n/timezone.h
index f7fda94..7557d44f 100644
--- a/base/i18n/timezone.h
+++ b/base/i18n/timezone.h
@@ -11,9 +11,12 @@
 
 namespace base {
 
-// Checks the system timezone and turns it into a two-character ASCII country
-// code. This may fail (for example, it will always fail on Android), in which
-// case it will return an empty string.
+// Checks the system timezone and turns it into a two-character ISO 3166 country
+// code. This may fail (for example, it used to always fail on Android), in
+// which case it will return an empty string. It'll also return an empty string
+// when the timezone is Etc/UTC or Etc/UCT, but will return 'GB" for Etc/GMT
+// because people in the UK tends to select Etc/GMT by mistake instead of
+// Europe/London (British Time).
 BASE_I18N_EXPORT std::string CountryCodeForCurrentTimezone();
 
 }  // namespace base
diff --git a/base/i18n/timezone_unittest.cc b/base/i18n/timezone_unittest.cc
index 2cdcc42..57467dc 100644
--- a/base/i18n/timezone_unittest.cc
+++ b/base/i18n/timezone_unittest.cc
@@ -11,10 +11,16 @@
 
 TEST(TimezoneTest, CountryCodeForCurrentTimezone) {
   std::string country_code = CountryCodeForCurrentTimezone();
-  // On some systems (such as Android or some flavors of Linux), icu may come up
-  // empty.
+  // On some systems (such as Android or some flavors of Linux), ICU may come up
+  // empty. With https://chromium-review.googlesource.com/c/512282/ , ICU will
+  // not fail any more. See also http://bugs.icu-project.org/trac/ticket/13208 .
+  // Even with that, ICU returns '001' (world) for region-agnostic timezones
+  // such as Etc/UTC and |CountryCodeForCurrentTimezone| returns an empty
+  // string so that the next fallback can be tried by a customer.
+  // TODO(jshin): Revise this to test for actual timezones using
+  // use ScopedRestoreICUDefaultTimezone.
   if (!country_code.empty())
-    EXPECT_EQ(2U, country_code.size());
+    EXPECT_EQ(2U, country_code.size()) << "country_code = " << country_code;
 }
 
 }  // namespace
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 92fccb9..a07bc92 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -1542,7 +1542,7 @@
 
   DCHECK(handle.chunk_seq);
   DCHECK(handle.chunk_index <= TraceBufferChunk::kMaxChunkIndex);
-  DCHECK(handle.event_index < TraceBufferChunk::kTraceBufferChunkSize);
+  DCHECK(handle.event_index <= TraceBufferChunk::kTraceBufferChunkSize - 1);
 
   if (thread_local_event_buffer_.Get()) {
     TraceEvent* trace_event =
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index 93438a6..7aaadb9 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -60,6 +60,8 @@
     "paint_text_blob_builder.h",
     "paint_typeface.cc",
     "paint_typeface.h",
+    "paint_typeface_transfer_cache_entry.cc",
+    "paint_typeface_transfer_cache_entry.h",
     "raw_memory_transfer_cache_entry.cc",
     "raw_memory_transfer_cache_entry.h",
     "record_paint_canvas.cc",
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index 06bf08fe..2db5b54 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -10,6 +10,7 @@
 #include "cc/paint/paint_flags.h"
 #include "cc/paint/paint_op_buffer.h"
 #include "cc/paint/paint_shader.h"
+#include "cc/paint/paint_typeface_transfer_cache_entry.h"
 #include "third_party/skia/include/core/SkFlattenableSerialization.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkRRect.h"
@@ -19,8 +20,6 @@
 namespace {
 
 uint32_t kMaxTypefacesCount = 128;
-size_t kMaxFilenameSize = 1024;
-size_t kMaxFamilyNameSize = 128;
 
 // If we have more than this many colors, abort deserialization.
 const size_t kMaxShaderColorsSupported = 10000;
@@ -276,79 +275,20 @@
   }
   typefaces->reserve(typefaces_count);
   for (uint32_t i = 0; i < typefaces_count; ++i) {
-    SkFontID id;
-    uint8_t type;
-    PaintTypeface typeface;
-    ReadSimple(&id);
-    ReadSimple(&type);
-    if (!valid_)
+    // TODO(vmpstr): This is meant to be transferred via a transfer cache, but
+    // for now just utilize the deserialization that the cache entry provides.
+    ServicePaintTypefaceTransferCacheEntry entry;
+    bool success = entry.Deserialize(
+        nullptr,
+        base::make_span(reinterpret_cast<uint8_t*>(const_cast<char*>(memory_)),
+                        remaining_bytes_));
+    if (!success) {
+      valid_ = false;
       return;
-    switch (static_cast<PaintTypeface::Type>(type)) {
-      case PaintTypeface::Type::kTestTypeface:
-        typeface = PaintTypeface::TestTypeface();
-        break;
-      case PaintTypeface::Type::kSkTypeface:
-        // TODO(vmpstr): This shouldn't ever happen once everything is
-        // implemented. So this should be a failure (ie |valid_| = false).
-        break;
-      case PaintTypeface::Type::kFontConfigInterfaceIdAndTtcIndex: {
-        int font_config_interface_id = 0;
-        int ttc_index = 0;
-        ReadSimple(&font_config_interface_id);
-        ReadSimple(&ttc_index);
-        if (!valid_)
-          return;
-        typeface = PaintTypeface::FromFontConfigInterfaceIdAndTtcIndex(
-            font_config_interface_id, ttc_index);
-        break;
-      }
-      case PaintTypeface::Type::kFilenameAndTtcIndex: {
-        size_t size;
-        ReadSimple(&size);
-        if (!valid_ || size > kMaxFilenameSize) {
-          SetInvalid();
-          return;
-        }
-
-        std::unique_ptr<char[]> buffer(new char[size]);
-        ReadData(size, buffer.get());
-        std::string filename(buffer.get(), size);
-
-        int ttc_index = 0;
-        ReadSimple(&ttc_index);
-        if (!valid_)
-          return;
-        typeface = PaintTypeface::FromFilenameAndTtcIndex(filename, ttc_index);
-        break;
-      }
-      case PaintTypeface::Type::kFamilyNameAndFontStyle: {
-        size_t size;
-        ReadSimple(&size);
-        if (!valid_ || size > kMaxFamilyNameSize) {
-          SetInvalid();
-          return;
-        }
-
-        std::unique_ptr<char[]> buffer(new char[size]);
-        ReadData(size, buffer.get());
-        std::string family_name(buffer.get(), size);
-
-        int weight = 0;
-        int width = 0;
-        SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant;
-        ReadSimple(&weight);
-        ReadSimple(&width);
-        ReadSimple(&slant);
-        if (!valid_)
-          return;
-
-        typeface = PaintTypeface::FromFamilyNameAndFontStyle(
-            family_name, SkFontStyle(weight, width, slant));
-        break;
-      }
     }
-    typeface.SetSkId(id);
-    typefaces->emplace_back(std::move(typeface));
+    typefaces->emplace_back(entry.typeface());
+    memory_ += entry.CachedSize();
+    remaining_bytes_ -= entry.CachedSize();
   }
 }
 
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index f5b1c1a..4674969 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -6,6 +6,7 @@
 
 #include "cc/paint/paint_flags.h"
 #include "cc/paint/paint_shader.h"
+#include "cc/paint/paint_typeface_transfer_cache_entry.h"
 #include "third_party/skia/include/core/SkFlattenableSerialization.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
 
@@ -144,36 +145,26 @@
   WriteSimple(static_cast<uint32_t>(typefaces.size()));
   for (const auto& typeface : typefaces) {
     DCHECK(typeface);
-    WriteSimple(typeface.sk_id());
-    WriteSimple(static_cast<uint8_t>(typeface.type()));
-    switch (typeface.type()) {
-      case PaintTypeface::Type::kTestTypeface:
-        // Nothing to serialize here.
-        break;
-      case PaintTypeface::Type::kSkTypeface:
-        // Nothing to do here. This should never be the case when everything is
-        // implemented. This should be a NOTREACHED() eventually.
-        break;
-      case PaintTypeface::Type::kFontConfigInterfaceIdAndTtcIndex:
-        WriteSimple(typeface.font_config_interface_id());
-        WriteSimple(typeface.ttc_index());
-        break;
-      case PaintTypeface::Type::kFilenameAndTtcIndex:
-        WriteSimple(typeface.filename().size());
-        WriteData(typeface.filename().size(), typeface.filename().data());
-        WriteSimple(typeface.ttc_index());
-        break;
-      case PaintTypeface::Type::kFamilyNameAndFontStyle:
-        WriteSimple(typeface.family_name().size());
-        WriteData(typeface.family_name().size(), typeface.family_name().data());
-        WriteSimple(typeface.font_style().weight());
-        WriteSimple(typeface.font_style().width());
-        WriteSimple(typeface.font_style().slant());
-        break;
+    // TODO(vmpstr): This is meant to be sent via a transfer cache, but for now
+    // just use the serialization of the client transfer cache entry.
+    ClientPaintTypefaceTransferCacheEntry entry(typeface);
+    size_t size = entry.SerializedSize();
+    if (size > remaining_bytes_) {
+      valid_ = false;
+      return;
     }
+    bool success = entry.Serialize(
+        base::make_span(reinterpret_cast<uint8_t*>(memory_), size));
+    if (!success) {
+      valid_ = false;
+      return;
+    }
+
+    memory_ += size;
+    remaining_bytes_ -= size;
+
 #if DCHECK_IS_ON()
-    if (typeface)
-      last_serialized_typeface_ids_.insert(typeface.sk_id());
+    last_serialized_typeface_ids_.insert(typeface.sk_id());
 #endif
   }
 }
@@ -195,6 +186,8 @@
 
 void PaintOpWriter::Write(const scoped_refptr<PaintTextBlob>& blob) {
   Write(blob->typefaces());
+  if (!valid_)
+    return;
   Write(blob->ToSkTextBlob());
 }
 
diff --git a/cc/paint/paint_typeface.h b/cc/paint/paint_typeface.h
index 56bae2b..0e2f370 100644
--- a/cc/paint/paint_typeface.h
+++ b/cc/paint/paint_typeface.h
@@ -18,7 +18,9 @@
     kSkTypeface,
     kFontConfigInterfaceIdAndTtcIndex,
     kFilenameAndTtcIndex,
-    kFamilyNameAndFontStyle
+    kFamilyNameAndFontStyle,
+    // We need to update this if the list is modified.
+    kLastType = kFamilyNameAndFontStyle
   };
 
   static PaintTypeface TestTypeface();
diff --git a/cc/paint/paint_typeface_transfer_cache_entry.cc b/cc/paint/paint_typeface_transfer_cache_entry.cc
new file mode 100644
index 0000000..b05649c7
--- /dev/null
+++ b/cc/paint/paint_typeface_transfer_cache_entry.cc
@@ -0,0 +1,228 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_typeface_transfer_cache_entry.h"
+
+namespace cc {
+namespace {
+
+size_t kMaxFilenameSize = 1024;
+size_t kMaxFamilyNameSize = 128;
+
+class DataWriter {
+ public:
+  explicit DataWriter(base::span<uint8_t> data) : data_(data) {}
+
+  template <typename T>
+  void WriteSimple(const T& val) {
+    DCHECK_LE(sizeof(T), data_.size());
+    *reinterpret_cast<T*>(data_.data()) = val;
+    data_ = data_.subspan(sizeof(T));
+  }
+  void WriteData(size_t bytes, const void* input) {
+    DCHECK_LE(bytes, data_.size());
+    memcpy(data_.data(), input, bytes);
+    data_ = data_.subspan(bytes);
+  }
+
+ private:
+  base::span<uint8_t> data_;
+};
+
+class SizeCounter {
+ public:
+  SizeCounter() = default;
+
+  template <typename T>
+  void WriteSimple(const T& val) {
+    size_ += sizeof(T);
+  }
+  void WriteData(size_t bytes, const void* input) { size_ += bytes; }
+  size_t size() const { return size_; }
+
+ private:
+  size_t size_ = 0;
+};
+
+}  // namespace
+
+ClientPaintTypefaceTransferCacheEntry::ClientPaintTypefaceTransferCacheEntry(
+    const PaintTypeface& typeface)
+    : typeface_(typeface) {
+  SizeCounter counter;
+  SerializeInternal(&counter);
+  size_ = counter.size();
+}
+
+ClientPaintTypefaceTransferCacheEntry::
+    ~ClientPaintTypefaceTransferCacheEntry() = default;
+
+TransferCacheEntryType ClientPaintTypefaceTransferCacheEntry::Type() const {
+  return TransferCacheEntryType::kPaintTypeface;
+}
+
+size_t ClientPaintTypefaceTransferCacheEntry::SerializedSize() const {
+  return size_;
+}
+
+bool ClientPaintTypefaceTransferCacheEntry::Serialize(
+    base::span<uint8_t> data) const {
+  DataWriter writer(data);
+  return SerializeInternal(&writer);
+}
+
+template <typename Writer>
+bool ClientPaintTypefaceTransferCacheEntry::SerializeInternal(
+    Writer* writer) const {
+  writer->WriteSimple(typeface_.sk_id());
+  writer->WriteSimple(static_cast<uint8_t>(typeface_.type()));
+  switch (typeface_.type()) {
+    case PaintTypeface::Type::kTestTypeface:
+      // Nothing to serialize here.
+      break;
+    case PaintTypeface::Type::kSkTypeface:
+      // Nothing to do here. This should never be the case when everything is
+      // implemented. This should be a NOTREACHED() eventually.
+      break;
+    case PaintTypeface::Type::kFontConfigInterfaceIdAndTtcIndex:
+      writer->WriteSimple(typeface_.font_config_interface_id());
+      writer->WriteSimple(typeface_.ttc_index());
+      break;
+    case PaintTypeface::Type::kFilenameAndTtcIndex:
+      writer->WriteSimple(typeface_.filename().size());
+      writer->WriteData(typeface_.filename().size(),
+                        typeface_.filename().data());
+      writer->WriteSimple(typeface_.ttc_index());
+      break;
+    case PaintTypeface::Type::kFamilyNameAndFontStyle:
+      writer->WriteSimple(typeface_.family_name().size());
+      writer->WriteData(typeface_.family_name().size(),
+                        typeface_.family_name().data());
+      writer->WriteSimple(typeface_.font_style().weight());
+      writer->WriteSimple(typeface_.font_style().width());
+      writer->WriteSimple(typeface_.font_style().slant());
+      break;
+  }
+  return true;
+}
+
+ServicePaintTypefaceTransferCacheEntry::
+    ServicePaintTypefaceTransferCacheEntry() = default;
+ServicePaintTypefaceTransferCacheEntry::
+    ~ServicePaintTypefaceTransferCacheEntry() = default;
+TransferCacheEntryType ServicePaintTypefaceTransferCacheEntry::Type() const {
+  return TransferCacheEntryType::kPaintTypeface;
+}
+
+size_t ServicePaintTypefaceTransferCacheEntry::CachedSize() const {
+  return size_;
+}
+
+bool ServicePaintTypefaceTransferCacheEntry::Deserialize(
+    GrContext* context,
+    base::span<uint8_t> data) {
+  data_ = data;
+  size_t initial_size = data_.size();
+
+  SkFontID id;
+  uint8_t type;
+  ReadSimple(&id);
+  ReadSimple(&type);
+  if (!valid_ || type > static_cast<uint8_t>(PaintTypeface::Type::kLastType)) {
+    valid_ = false;
+    return false;
+  }
+  switch (static_cast<PaintTypeface::Type>(type)) {
+    case PaintTypeface::Type::kTestTypeface:
+      typeface_ = PaintTypeface::TestTypeface();
+      break;
+    case PaintTypeface::Type::kSkTypeface:
+      // TODO(vmpstr): This shouldn't ever happen once everything is
+      // implemented. So this should be a failure (ie |valid_| = false).
+      break;
+    case PaintTypeface::Type::kFontConfigInterfaceIdAndTtcIndex: {
+      int font_config_interface_id = 0;
+      int ttc_index = 0;
+      ReadSimple(&font_config_interface_id);
+      ReadSimple(&ttc_index);
+      if (!valid_)
+        return false;
+      typeface_ = PaintTypeface::FromFontConfigInterfaceIdAndTtcIndex(
+          font_config_interface_id, ttc_index);
+      break;
+    }
+    case PaintTypeface::Type::kFilenameAndTtcIndex: {
+      size_t size;
+      ReadSimple(&size);
+      if (!valid_ || size > kMaxFilenameSize) {
+        valid_ = false;
+        return false;
+      }
+
+      std::unique_ptr<char[]> buffer(new char[size]);
+      ReadData(size, buffer.get());
+      std::string filename(buffer.get(), size);
+
+      int ttc_index = 0;
+      ReadSimple(&ttc_index);
+      if (!valid_)
+        return false;
+      typeface_ = PaintTypeface::FromFilenameAndTtcIndex(filename, ttc_index);
+      break;
+    }
+    case PaintTypeface::Type::kFamilyNameAndFontStyle: {
+      size_t size;
+      ReadSimple(&size);
+      if (!valid_ || size > kMaxFamilyNameSize) {
+        valid_ = false;
+        return false;
+      }
+
+      std::unique_ptr<char[]> buffer(new char[size]);
+      ReadData(size, buffer.get());
+      std::string family_name(buffer.get(), size);
+
+      int weight = 0;
+      int width = 0;
+      SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant;
+      ReadSimple(&weight);
+      ReadSimple(&width);
+      ReadSimple(&slant);
+      if (!valid_)
+        return false;
+
+      typeface_ = PaintTypeface::FromFamilyNameAndFontStyle(
+          family_name, SkFontStyle(weight, width, slant));
+      break;
+    }
+  }
+  typeface_.SetSkId(id);
+
+  // Set the size to however much data we read.
+  size_ = initial_size - data_.size();
+  data_ = base::span<uint8_t>(nullptr);
+  return valid_;
+}
+
+template <typename T>
+void ServicePaintTypefaceTransferCacheEntry::ReadSimple(T* val) {
+  if (data_.size() < sizeof(T))
+    valid_ = false;
+  if (!valid_)
+    return;
+  *val = *reinterpret_cast<T*>(data_.data());
+  data_ = data_.subspan(sizeof(T));
+}
+
+void ServicePaintTypefaceTransferCacheEntry::ReadData(size_t bytes,
+                                                      void* data) {
+  if (data_.size() < bytes)
+    valid_ = false;
+  if (!valid_)
+    return;
+  memcpy(data, data_.data(), bytes);
+  data_ = data_.subspan(bytes);
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_typeface_transfer_cache_entry.h b/cc/paint/paint_typeface_transfer_cache_entry.h
new file mode 100644
index 0000000..d4249b2
--- /dev/null
+++ b/cc/paint/paint_typeface_transfer_cache_entry.h
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_PAINT_TYPEFACE_TRANSFER_CACHE_ENTRY_H_
+#define CC_PAINT_PAINT_TYPEFACE_TRANSFER_CACHE_ENTRY_H_
+
+#include "base/containers/span.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_typeface.h"
+#include "cc/paint/transfer_cache_entry.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT ClientPaintTypefaceTransferCacheEntry
+    : public ClientTransferCacheEntry {
+ public:
+  explicit ClientPaintTypefaceTransferCacheEntry(const PaintTypeface& typeface);
+  ~ClientPaintTypefaceTransferCacheEntry() final;
+  TransferCacheEntryType Type() const final;
+  size_t SerializedSize() const final;
+  bool Serialize(base::span<uint8_t> data) const final;
+
+ private:
+  template <typename Writer>
+  bool SerializeInternal(Writer* writer) const;
+
+  const PaintTypeface typeface_;
+  size_t size_ = 0u;
+};
+
+class CC_PAINT_EXPORT ServicePaintTypefaceTransferCacheEntry
+    : public ServiceTransferCacheEntry {
+ public:
+  ServicePaintTypefaceTransferCacheEntry();
+  ~ServicePaintTypefaceTransferCacheEntry() final;
+  TransferCacheEntryType Type() const final;
+  size_t CachedSize() const final;
+  bool Deserialize(GrContext* context, base::span<uint8_t> data) final;
+
+  const PaintTypeface& typeface() const { return typeface_; }
+
+ private:
+  template <typename T>
+  void ReadSimple(T* val);
+
+  void ReadData(size_t bytes, void* data);
+
+  PaintTypeface typeface_;
+  size_t size_ = 0;
+  bool valid_ = true;
+  base::span<uint8_t> data_;
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_PAINT_TYPEFACE_TRANSFER_CACHE_ENTRY_H_
diff --git a/cc/paint/transfer_cache_entry.cc b/cc/paint/transfer_cache_entry.cc
index ff4f0a5..fe407dd7 100644
--- a/cc/paint/transfer_cache_entry.cc
+++ b/cc/paint/transfer_cache_entry.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "cc/paint/image_transfer_cache_entry.h"
+#include "cc/paint/paint_typeface_transfer_cache_entry.h"
 #include "cc/paint/raw_memory_transfer_cache_entry.h"
 
 namespace cc {
@@ -19,6 +20,8 @@
       return std::make_unique<ServiceRawMemoryTransferCacheEntry>();
     case TransferCacheEntryType::kImage:
       return std::make_unique<ServiceImageTransferCacheEntry>();
+    case TransferCacheEntryType::kPaintTypeface:
+      return std::make_unique<ServicePaintTypefaceTransferCacheEntry>();
   }
 
   NOTREACHED();
diff --git a/cc/paint/transfer_cache_entry.h b/cc/paint/transfer_cache_entry.h
index 0d14361..ed09802 100644
--- a/cc/paint/transfer_cache_entry.h
+++ b/cc/paint/transfer_cache_entry.h
@@ -23,8 +23,9 @@
 enum class TransferCacheEntryType {
   kRawMemory,
   kImage,
+  kPaintTypeface,
   // Add new entries above this line, make sure to update kLast.
-  kLast = kImage,
+  kLast = kPaintTypeface,
 };
 
 // An interface used on the client to serialize a transfer cache entry
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 8a3a457..a5925c2 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2922,6 +2922,11 @@
      flag_descriptions::kEnableAutofillCreditCardUploadNewUiName,
      flag_descriptions::kEnableAutofillCreditCardUploadNewUiDescription,
      kOsDesktop, FEATURE_VALUE_TYPE(autofill::kAutofillUpstreamShowNewUi)},
+    {"enable-autofill-credit-card-upload-send-pan-first-six",
+     flag_descriptions::kEnableAutofillCreditCardUploadSendPanFirstSixName,
+     flag_descriptions::
+         kEnableAutofillCreditCardUploadSendPanFirstSixDescription,
+     kOsAll, FEATURE_VALUE_TYPE(autofill::kAutofillUpstreamSendPanFirstSix)},
     {"enable-autofill-credit-card-ablation-experiment",
      flag_descriptions::kEnableAutofillCreditCardAblationExperimentDisplayName,
      flag_descriptions::kEnableAutofillCreditCardAblationExperimentDescription,
diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc
index 2923b0a..464d88b 100644
--- a/chrome/browser/autocomplete/search_provider_unittest.cc
+++ b/chrome/browser/autocomplete/search_provider_unittest.cc
@@ -3290,18 +3290,6 @@
       GURL("https://www.google.com/complete/search"), &google_template_url,
       metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
 
-  // Non-HTTP page URL on different domain, yet with feature flag to allow
-  // this turned off.
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndDisableFeature(
-        omnibox::kSearchProviderContextAllowHttpsUrls);
-    EXPECT_FALSE(SearchProvider::CanSendURL(
-        GURL("https://www.notgoogle.com/search"),
-        GURL("https://www.google.com/complete/search"), &google_template_url,
-        metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
-  }
-
   // Non-HTTPS provider.
   EXPECT_FALSE(SearchProvider::CanSendURL(
       GURL("http://www.google.com/search"),
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 6f06d96..e5fafce 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -193,12 +193,17 @@
   system_clock_.reset();
 }
 
-void BrowserProcessPlatformPart::SetCompatibleCrosComponentPath(
+void BrowserProcessPlatformPart::RegisterCompatibleCrosComponentPath(
     const std::string& name,
     const base::FilePath& path) {
   compatible_cros_components_[name] = path;
 }
 
+void BrowserProcessPlatformPart::UnregisterCompatibleCrosComponentPath(
+    const std::string& name) {
+  compatible_cros_components_.erase(name);
+}
+
 bool BrowserProcessPlatformPart::IsCompatibleCrosComponent(
     const std::string& name) const {
   return compatible_cros_components_.count(name) > 0;
diff --git a/chrome/browser/browser_process_platform_part_chromeos.h b/chrome/browser/browser_process_platform_part_chromeos.h
index 86e57d54f..a118650 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.h
+++ b/chrome/browser/browser_process_platform_part_chromeos.h
@@ -105,8 +105,11 @@
   void DestroySystemClock();
 
   // Saves the name and install path of a compatible component.
-  void SetCompatibleCrosComponentPath(const std::string& name,
-                                      const base::FilePath& path);
+  void RegisterCompatibleCrosComponentPath(const std::string& name,
+                                           const base::FilePath& path);
+
+  // Removes the name and install path entry of a component.
+  void UnregisterCompatibleCrosComponentPath(const std::string& name);
 
   // Checks if the current installed component is compatible given a component
   // |name|. If compatible, sets |path| to be its installed path.
diff --git a/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.cc b/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.cc
index 12bdf5e..ee6c8923 100644
--- a/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.cc
+++ b/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.cc
@@ -16,8 +16,14 @@
 
 void ChromeComponentUpdaterServiceProviderDelegate::LoadComponent(
     const std::string& name,
-    const base::Callback<void(const std::string&)>& load_callback) {
-  component_updater::CrOSComponent::LoadComponent(name, load_callback);
+    base::OnceCallback<void(const std::string&)> load_callback) {
+  component_updater::CrOSComponent::LoadComponent(name,
+                                                  std::move(load_callback));
+}
+
+bool ChromeComponentUpdaterServiceProviderDelegate::UnloadComponent(
+    const std::string& name) {
+  return component_updater::CrOSComponent::UnloadComponent(name);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.h b/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.h
index 9a50600..f8a146a 100644
--- a/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.h
+++ b/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.h
@@ -20,7 +20,8 @@
   // ComponentUpdaterServiceProvider::Delegate:
   void LoadComponent(
       const std::string& name,
-      const base::Callback<void(const std::string&)>& load_callback) override;
+      base::OnceCallback<void(const std::string&)> load_callback) override;
+  bool UnloadComponent(const std::string& name) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ChromeComponentUpdaterServiceProviderDelegate);
diff --git a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
index a3e56a3e..83d3662a 100644
--- a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
@@ -429,10 +429,6 @@
     WizardController* controller = WizardController::default_controller();
     if (controller && controller->current_screen())
       controller->current_screen()->Hide();
-
-    if (LoginDisplayHost::default_host())
-      LoginDisplayHost::default_host()->Finalize(base::OnceClosure());
-    base::RunLoop().RunUntilIdle();
   }
 
   void ExpectSuccessfulLogin(const UserContext& user_context) {
diff --git a/chrome/browser/chromeos/login/login_browsertest.cc b/chrome/browser/chromeos/login/login_browsertest.cc
index 830ac748..8d67451 100644
--- a/chrome/browser/chromeos/login/login_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_browsertest.cc
@@ -88,12 +88,6 @@
     command_line->AppendSwitch(switches::kForceLoginManagerInTests);
   }
 
-  void TearDownOnMainThread() override {
-    // Close the login manager, which otherwise holds a KeepAlive that is not
-    // cleared in time by the end of the test.
-    LoginDisplayHost::default_host()->Finalize(base::OnceClosure());
-  }
-
   void SetUpOnMainThread() override {
     LoginDisplayHostWebUI::DisableRestrictiveProxyCheckForTest();
   }
@@ -251,9 +245,6 @@
   EXPECT_TRUE(ui_test_utils::SendMouseMoveSync(gfx::Point()));
   EXPECT_TRUE(ash::Shell::Get()->cursor_manager()->IsCursorVisible());
 
-  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
-      FROM_HERE, LoginDisplayHost::default_host());
-
   TestSystemTrayIsVisible(false);
 }
 
diff --git a/chrome/browser/chromeos/login/login_manager_test.cc b/chrome/browser/chromeos/login/login_manager_test.cc
index 330e36b8..cfb289bb 100644
--- a/chrome/browser/chromeos/login/login_manager_test.cc
+++ b/chrome/browser/chromeos/login/login_manager_test.cc
@@ -98,9 +98,7 @@
 
 void LoginManagerTest::TearDownOnMainThread() {
   MixinBasedBrowserTest::TearDownOnMainThread();
-  if (LoginDisplayHost::default_host())
-    LoginDisplayHost::default_host()->Finalize(base::OnceClosure());
-  base::RunLoop().RunUntilIdle();
+
   EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
 }
 
diff --git a/chrome/browser/chromeos/login/test/oobe_base_test.cc b/chrome/browser/chromeos/login/test/oobe_base_test.cc
index e15b1253..933c107 100644
--- a/chrome/browser/chromeos/login/test/oobe_base_test.cc
+++ b/chrome/browser/chromeos/login/test/oobe_base_test.cc
@@ -130,12 +130,6 @@
 }
 
 void OobeBaseTest::TearDownOnMainThread() {
-  // If the login display is still showing, exit gracefully.
-  if (LoginDisplayHost::default_host()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&chrome::AttemptExit));
-    content::RunMessageLoop();
-  }
   EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
 
   ExtensionApiTest::TearDownOnMainThread();
diff --git a/chrome/browser/chromeos/login/test/wizard_in_process_browser_test.cc b/chrome/browser/chromeos/login/test/wizard_in_process_browser_test.cc
index 5c034b5..b5b7daa9 100644
--- a/chrome/browser/chromeos/login/test/wizard_in_process_browser_test.cc
+++ b/chrome/browser/chromeos/login/test/wizard_in_process_browser_test.cc
@@ -46,9 +46,16 @@
 void WizardInProcessBrowserTest::TearDownOnMainThread() {
   ASSERT_TRUE(base::MessageLoopForUI::IsCurrent());
 
-  // LoginDisplayHost owns controllers and all windows.
-  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, host_);
-  base::RunLoop().RunUntilIdle();
+  if (!host_)
+    return;
+
+  // LoginDisplayHost owns controllers and all windows. It needs to be destroyed
+  // here because the derived tests have clean-up code assuming LoginDisplayHost
+  // is gone.
+  base::RunLoop run_loop;
+  host_->Finalize(run_loop.QuitClosure());
+  run_loop.Run();
+  host_ = nullptr;
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/captive_portal_window_browsertest.cc b/chrome/browser/chromeos/login/ui/captive_portal_window_browsertest.cc
index 5568418..0399bf52 100644
--- a/chrome/browser/chromeos/login/ui/captive_portal_window_browsertest.cc
+++ b/chrome/browser/chromeos/login/ui/captive_portal_window_browsertest.cc
@@ -77,26 +77,19 @@
   }
 
   void SetUpOnMainThread() override {
-    host_ = LoginDisplayHost::default_host();
     content::WebContents* web_contents =
-        host_->GetWebUILoginView()->GetWebContents();
+        LoginDisplayHost::default_host()->GetWebUILoginView()->GetWebContents();
     captive_portal_window_proxy_.reset(
         new CaptivePortalWindowProxy(&delegate_, web_contents));
   }
 
   void TearDownOnMainThread() override {
     captive_portal_window_proxy_.reset();
-
-    ASSERT_TRUE(base::MessageLoopForUI::IsCurrent());
-    base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, host_);
-    base::RunLoop().RunUntilIdle();
   }
 
  private:
   std::unique_ptr<CaptivePortalWindowProxy> captive_portal_window_proxy_;
   CaptivePortalWindowProxyStubDelegate delegate_;
-
-  LoginDisplayHost* host_;
 };
 
 IN_PROC_BROWSER_TEST_F(CaptivePortalWindowTest, Show) {
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
index 95a00c8..af6ac50 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -18,7 +18,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
@@ -435,11 +434,8 @@
 
   ui::InputDeviceManager::GetInstance()->AddObserver(this);
 
-  // We need to listen to CLOSE_ALL_BROWSERS_REQUEST but not APP_TERMINATING
-  // because/ APP_TERMINATING will never be fired as long as this keeps
-  // ref-count. CLOSE_ALL_BROWSERS_REQUEST is safe here because there will be no
-  // browser instance that will block the shutdown.
-  registrar_.Add(this, chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
+  // Close the login screen on NOTIFICATION_APP_TERMINATING.
+  registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
                  content::NotificationService::AllSources());
 
   // NOTIFICATION_BROWSER_OPENED is issued after browser is created, but
@@ -600,13 +596,13 @@
 
   switch (finalize_animation_type_) {
     case ANIMATION_NONE:
-      ShutdownDisplayHost(false);
+      ShutdownDisplayHost();
       break;
     case ANIMATION_WORKSPACE:
       if (ash::Shell::HasInstance())
         ScheduleWorkspaceAnimation();
 
-      ShutdownDisplayHost(false);
+      ShutdownDisplayHost();
       break;
     case ANIMATION_FADE_OUT:
       // Display host is deleted once animation is completed
@@ -901,13 +897,13 @@
                       content::NotificationService::AllSources());
     registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
                       content::NotificationService::AllSources());
-  } else if (type == chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST) {
-    ShutdownDisplayHost(true);
+  } else if (type == chrome::NOTIFICATION_APP_TERMINATING) {
+    ShutdownDisplayHost();
   } else if (type == chrome::NOTIFICATION_BROWSER_OPENED && session_starting_) {
     // Browsers created before session start (windows opened by extensions, for
     // example) are ignored.
     OnBrowserCreated();
-    registrar_.Remove(this, chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
+    registrar_.Remove(this, chrome::NOTIFICATION_APP_TERMINATING,
                       content::NotificationService::AllSources());
     registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_OPENED,
                       content::NotificationService::AllSources());
@@ -1034,21 +1030,19 @@
 ////////////////////////////////////////////////////////////////////////////////
 // LoginDisplayHostWebUI, MultiUserWindowManager::Observer:
 void LoginDisplayHostWebUI::OnUserSwitchAnimationFinished() {
-  ShutdownDisplayHost(false);
+  ShutdownDisplayHost();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // LoginDisplayHostWebUI, private
 
-void LoginDisplayHostWebUI::ShutdownDisplayHost(bool post_quit_task) {
+void LoginDisplayHostWebUI::ShutdownDisplayHost() {
   if (shutting_down_)
     return;
 
   shutting_down_ = true;
   registrar_.RemoveAll();
   base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
-  if (post_quit_task)
-    base::RunLoop::QuitCurrentWhenIdleDeprecated();
 }
 
 void LoginDisplayHostWebUI::ScheduleWorkspaceAnimation() {
@@ -1069,14 +1063,14 @@
   // is created before session start, which triggers the close of the login
   // window. In this case, we should shut down the display host directly.
   if (!login_window_) {
-    ShutdownDisplayHost(false);
+    ShutdownDisplayHost();
     return;
   }
   ui::Layer* layer = login_window_->GetLayer();
   ui::ScopedLayerAnimationSettings animation(layer->GetAnimator());
   animation.AddObserver(new AnimationObserver(
       base::Bind(&LoginDisplayHostWebUI::ShutdownDisplayHost,
-                 animation_weak_ptr_factory_.GetWeakPtr(), false)));
+                 animation_weak_ptr_factory_.GetWeakPtr())));
   animation.SetTransitionDuration(
       base::TimeDelta::FromMilliseconds(animation_speed_ms));
   layer->SetOpacity(0);
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.h b/chrome/browser/chromeos/login/ui/login_display_host_webui.h
index 01da177..e45df00 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.h
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.h
@@ -153,8 +153,7 @@
   };
 
   // Marks display host for deletion.
-  // If |post_quit_task| is true also posts Quit task to the MessageLoop.
-  void ShutdownDisplayHost(bool post_quit_task);
+  void ShutdownDisplayHost();
 
   // Schedules workspace transition animation.
   void ScheduleWorkspaceAnimation();
diff --git a/chrome/browser/component_updater/cros_component_installer.cc b/chrome/browser/component_updater/cros_component_installer.cc
index 9304f9cf..af82d18 100644
--- a/chrome/browser/component_updater/cros_component_installer.cc
+++ b/chrome/browser/component_updater/cros_component_installer.cc
@@ -52,6 +52,18 @@
 
 namespace component_updater {
 
+namespace {
+// TODO(xiaochu): add metrics for component usage (crbug.com/793052).
+static void LogCustomUninstall(base::Optional<bool> result) {}
+static std::string GenerateId(const std::string& sha2hashstr) {
+  // kIdSize is the count of a pair of hex in the sha2hash array.
+  // In string representation of sha2hash, size is doubled since each hex is
+  // represented by a single char.
+  return crx_file::id_util::GenerateIdFromHex(
+      sha2hashstr.substr(0, crx_file::id_util::kIdSize * 2));
+}
+}  // namespace
+
 using ConfigMap = std::map<std::string, std::map<std::string, std::string>>;
 
 ComponentConfig::ComponentConfig(const std::string& name,
@@ -102,6 +114,11 @@
 }
 
 void CrOSComponentInstallerPolicy::OnCustomUninstall() {
+  g_browser_process->platform_part()->UnregisterCompatibleCrosComponentPath(
+      name);
+
+  chromeos::DBusThreadManager::Get()->GetImageLoaderClient()->UnmountComponent(
+      name, base::BindOnce(&LogCustomUninstall));
 }
 
 void CrOSComponentInstallerPolicy::ComponentReady(
@@ -111,7 +128,7 @@
   std::string min_env_version;
   if (manifest && manifest->GetString("min_env_version", &min_env_version)) {
     if (IsCompatible(env_version, min_env_version)) {
-      g_browser_process->platform_part()->SetCompatibleCrosComponentPath(
+      g_browser_process->platform_part()->RegisterCompatibleCrosComponentPath(
           GetName(), path);
     }
   }
@@ -173,21 +190,18 @@
     const std::string& name,
     base::OnceCallback<void(const std::string&)> load_callback) {
   DCHECK(g_browser_process->platform_part()->IsCompatibleCrosComponent(name));
-  chromeos::ImageLoaderClient* loader =
-      chromeos::DBusThreadManager::Get()->GetImageLoaderClient();
-  if (loader) {
-    base::FilePath path;
-    path = g_browser_process->platform_part()->GetCompatibleCrosComponentPath(
-        name);
-    // path is empty if no compatible component is available to load.
-    if (!path.empty()) {
-      loader->LoadComponentAtPath(
-          name, path, base::BindOnce(&LoadResult, std::move(load_callback)));
-      return;
-    }
+  const base::FilePath path =
+      g_browser_process->platform_part()->GetCompatibleCrosComponentPath(name);
+  // path is empty if no compatible component is available to load.
+  if (!path.empty()) {
+    chromeos::DBusThreadManager::Get()
+        ->GetImageLoaderClient()
+        ->LoadComponentAtPath(
+            name, path, base::BindOnce(&LoadResult, std::move(load_callback)));
+  } else {
+    base::PostTask(FROM_HERE,
+                   base::BindOnce(std::move(load_callback), std::string()));
   }
-  base::PostTask(FROM_HERE,
-                 base::BindOnce(std::move(load_callback), std::string()));
 }
 
 // It calls LoadComponentInternal to load the installed component.
@@ -223,20 +237,19 @@
     base::OnceCallback<void(const std::string&)> load_callback) {
   const ConfigMap components = CONFIG_MAP_CONTENT;
   const auto it = components.find(name);
-  if (name.empty() || it == components.end()) {
+  if (it == components.end()) {
     base::PostTask(FROM_HERE,
                    base::BindOnce(std::move(load_callback), std::string()));
     return;
   }
   ComponentConfig config(it->first, it->second.find("env_version")->second,
                          it->second.find("sha2hashstr")->second);
-  RegisterComponent(cus, config,
-                    base::BindOnce(RegisterResult, cus,
-                                   crx_file::id_util::GenerateIdFromHex(
-                                       it->second.find("sha2hashstr")->second)
-                                       .substr(0, 32),
-                                   base::BindOnce(InstallResult, name,
-                                                  std::move(load_callback))));
+  RegisterComponent(
+      cus, config,
+      base::BindOnce(
+          RegisterResult, cus,
+          GenerateId(it->second.find("sha2hashstr")->second),
+          base::BindOnce(InstallResult, name, std::move(load_callback))));
 }
 
 void CrOSComponent::LoadComponent(
@@ -252,6 +265,19 @@
   }
 }
 
+bool CrOSComponent::UnloadComponent(const std::string& name) {
+  const ConfigMap components = CONFIG_MAP_CONTENT;
+  const auto it = components.find(name);
+  if (it == components.end()) {
+    // Component |name| does not exist.
+    return false;
+  }
+  component_updater::ComponentUpdateService* updater =
+      g_browser_process->component_updater();
+  const std::string id = GenerateId(it->second.find("sha2hashstr")->second);
+  return updater->UnregisterComponent(id);
+}
+
 std::vector<ComponentConfig> CrOSComponent::GetInstalledComponents() {
   std::vector<ComponentConfig> configs;
   base::FilePath root;
diff --git a/chrome/browser/component_updater/cros_component_installer.h b/chrome/browser/component_updater/cros_component_installer.h
index 9afcdd93..14ddfcd 100644
--- a/chrome/browser/component_updater/cros_component_installer.h
+++ b/chrome/browser/component_updater/cros_component_installer.h
@@ -83,10 +83,16 @@
 // This class contains functions used to register and install a component.
 class CrOSComponent {
  public:
+  // Installs a component and keeps it up-to-date.
   static void LoadComponent(
       const std::string& name,
       base::OnceCallback<void(const std::string&)> load_callback);
 
+  // Stops updating and removes a component.
+  // Returns true if the component was successfully unloaded
+  // or false if it couldn't be unloaded or already wasn't loaded.
+  static bool UnloadComponent(const std::string& name);
+
   // Returns all installed components.
   static std::vector<ComponentConfig> GetInstalledComponents();
 
@@ -94,7 +100,6 @@
   static void RegisterComponents(const std::vector<ComponentConfig>& configs);
 
  private:
-  CrOSComponent() {}
   static void RegisterResult(ComponentUpdateService* cus,
                              const std::string& id,
                              update_client::Callback install_callback);
diff --git a/chrome/browser/component_updater/cros_component_installer_unittest.cc b/chrome/browser/component_updater/cros_component_installer_unittest.cc
index 16992ac..7f8f8d5 100644
--- a/chrome/browser/component_updater/cros_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/cros_component_installer_unittest.cc
@@ -64,9 +64,11 @@
             std::string());
 
   const base::FilePath kPath("/component/path/v0");
-  bppp.SetCompatibleCrosComponentPath(kComponent, kPath);
+  bppp.RegisterCompatibleCrosComponentPath(kComponent, kPath);
   EXPECT_TRUE(bppp.IsCompatibleCrosComponent(kComponent));
-  EXPECT_EQ(bppp.GetCompatibleCrosComponentPath("a"), kPath);
+  EXPECT_EQ(bppp.GetCompatibleCrosComponentPath(kComponent), kPath);
+  bppp.UnregisterCompatibleCrosComponentPath(kComponent);
+  EXPECT_FALSE(bppp.IsCompatibleCrosComponent(kComponent));
 }
 
 TEST_F(CrOSComponentInstallerTest, ComponentReadyCorrectManifest) {
diff --git a/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h b/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h
index 9b63355..7dc82e0f 100644
--- a/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h
+++ b/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h
@@ -211,11 +211,16 @@
       *error = kCheckedError;
       return false;
     }
-    // If the item was not checked and it is updated to be checked, set it to be
-    // checked. If the radio item was unchecked, nothing should happen. The
-    // radio item should remain checked because there should always be one item
-    // checked in the radio list.
-    if (checked && !item->checked()) {
+
+    const bool should_toggle_checked =
+        // If radio item was unchecked nothing should happen. The radio item
+        // should remain checked because there should always be one item checked
+        // in the radio list.
+        (item->type() == MenuItem::RADIO && checked) ||
+        // Checkboxes are always updated.
+        item->type() == MenuItem::CHECKBOX;
+
+    if (should_toggle_checked) {
       if (!item->SetChecked(checked)) {
         *error = kCheckedError;
         return false;
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
index a3661f06..79e0b90 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc
@@ -13,7 +13,6 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/test/https_forwarder.h"
-#include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/policy/affiliation_test_helper.h"
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
 #include "chrome/browser/extensions/extension_apitest.h"
@@ -37,7 +36,6 @@
 #include "crypto/scoped_test_system_nss_key_slot.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/test_extension_registry_observer.h"
-#include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/test/result_catcher.h"
 #include "google_apis/gaia/fake_gaia.h"
 #include "google_apis/gaia/gaia_constants.h"
@@ -325,10 +323,6 @@
   void TearDownOnMainThread() override {
     ExtensionApiTest::TearDownOnMainThread();
 
-    if (chromeos::LoginDisplayHost::default_host())
-      chromeos::LoginDisplayHost::default_host()->Finalize(base::OnceClosure());
-    base::RunLoop().RunUntilIdle();
-
     if (GetParam().system_token_ == SYSTEM_TOKEN_EXISTS) {
       base::RunLoop loop;
       content::BrowserThread::PostTask(
@@ -400,7 +394,6 @@
   }
 
  private:
-
   void SetUpTestSystemSlotOnIO(const base::Closure& done_callback) {
     test_system_slot_.reset(new crypto::ScopedTestSystemNSSKeySlot());
     ASSERT_TRUE(test_system_slot_->ConstructedSuccessfully());
diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc
index 30e3ef8..7766a6f6 100644
--- a/chrome/browser/extensions/extension_context_menu_browsertest.cc
+++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc
@@ -852,3 +852,41 @@
   browser()->profile()->DestroyOffTheRecordProfile();
   ASSERT_EQ(1u, GetItems().size());
 }
+
+// Tests updating checkboxes' checked state to true and false.
+IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, UpdateCheckboxes) {
+  ExtensionTestMessageListener listener_context_menu_created("Menu created",
+                                                             false);
+
+  const extensions::Extension* extension =
+      LoadContextMenuExtension("checkboxes");
+  ASSERT_TRUE(extension);
+
+  ASSERT_TRUE(listener_context_menu_created.WaitUntilSatisfied());
+
+  GURL page_url("http://www.google.com");
+
+  // Create and build our test context menu.
+  std::unique_ptr<TestRenderViewContextMenu> menu(
+      TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
+                                        GURL()));
+
+  VerifyRadioItemSelectionState(menu.get(), extension->id(), "checkbox1",
+                                false);
+  VerifyRadioItemSelectionState(menu.get(), extension->id(), "checkbox2", true);
+
+  ExtensionTestMessageListener listener_item1_clicked("onclick normal item",
+                                                      false);
+  ExtensionTestMessageListener listener_unchecked_checkbox2(
+      "checkbox2 unchecked", false);
+  // Clicking the regular item calls chrome.contextMenus.update to uncheck the
+  // second checkbox item.
+  ExecuteCommand(menu.get(), extension->id(), "item1");
+  ASSERT_TRUE(listener_item1_clicked.WaitUntilSatisfied());
+  ASSERT_TRUE(listener_unchecked_checkbox2.WaitUntilSatisfied());
+
+  VerifyRadioItemSelectionState(menu.get(), extension->id(), "checkbox1",
+                                false);
+  VerifyRadioItemSelectionState(menu.get(), extension->id(), "checkbox2",
+                                false);
+}
diff --git a/chrome/browser/extensions/launch_util.cc b/chrome/browser/extensions/launch_util.cc
index 23986968..024624e 100644
--- a/chrome/browser/extensions/launch_util.cc
+++ b/chrome/browser/extensions/launch_util.cc
@@ -98,10 +98,7 @@
   LaunchContainer manifest_launch_container =
       AppLaunchInfo::GetLaunchContainer(extension);
 
-  const LaunchContainer kInvalidLaunchContainer =
-      static_cast<LaunchContainer>(-1);
-
-  LaunchContainer result = kInvalidLaunchContainer;
+  base::Optional<LaunchContainer> result;
 
   if (manifest_launch_container == LAUNCH_CONTAINER_PANEL) {
     // Apps with app.launch.container = 'panel' should always respect the
@@ -137,12 +134,12 @@
   }
 
   // All paths should set |result|.
-  if (result == kInvalidLaunchContainer) {
+  if (!result) {
     DLOG(FATAL) << "Failed to set a launch container.";
     result = LAUNCH_CONTAINER_TAB;
   }
 
-  return result;
+  return *result;
 }
 
 bool HasPreferredLaunchContainer(const ExtensionPrefs* prefs,
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index a7fc91c..8a750c7 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -299,6 +299,14 @@
     "If enabled, displays a new save card bubble/infobar design when offering "
     "to upload credit cards to Google Payments.";
 
+const char kEnableAutofillCreditCardUploadSendPanFirstSixName[] =
+    "Send first six digits of PAN when deciding whether to offer Autofill "
+    "credit card upload";
+const char kEnableAutofillCreditCardUploadSendPanFirstSixDescription[] =
+    "If enabled, when deciding whether to offer credit card upload to Google "
+    "Payments, sends the first six digits of the card number to avoid cases "
+    "where card upload is likely to fail.";
+
 const char kEnableAutofillSendBillingCustomerNumberName[] =
     "Enable autofill sending billing customer number when calling Google "
     "Payments";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 68ef234e..524022a 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -210,6 +210,9 @@
 extern const char kEnableAutofillCreditCardUploadNewUiName[];
 extern const char kEnableAutofillCreditCardUploadNewUiDescription[];
 
+extern const char kEnableAutofillCreditCardUploadSendPanFirstSixName[];
+extern const char kEnableAutofillCreditCardUploadSendPanFirstSixDescription[];
+
 extern const char kEnableAutofillSendBillingCustomerNumberName[];
 extern const char kEnableAutofillSendBillingCustomerNumberDescription[];
 
diff --git a/chrome/browser/resources/md_extensions/item.html b/chrome/browser/resources/md_extensions/item.html
index 27f2390..c9c013e5 100644
--- a/chrome/browser/resources/md_extensions/item.html
+++ b/chrome/browser/resources/md_extensions/item.html
@@ -208,7 +208,7 @@
               if="[[computeSourceIndicatorIcon_(data.*)]]">
             <div id="source-indicator">
               <div class="source-icon-wrapper" role="img"
-                  aria-labelledby="source-indicator-text">
+                  aria-label$="[[computeSourceIndicatorText_(data.*)]]">
                 <iron-icon icon="[[computeSourceIndicatorIcon_(data.*)]]">
                 </iron-icon>
               </div>
diff --git a/chrome/browser/service_process/service_process_control_browsertest.cc b/chrome/browser/service_process/service_process_control_browsertest.cc
index 00436d9..393b401f 100644
--- a/chrome/browser/service_process/service_process_control_browsertest.cc
+++ b/chrome/browser/service_process/service_process_control_browsertest.cc
@@ -220,8 +220,8 @@
 
 // This tests the case when a service process is launched when the browser
 // starts but we try to launch it again while setting up Cloud Print.
-// Flaky on Mac. Flaky on Windows. http://crbug.com/517420
-#if defined(OS_MACOSX) || defined(OS_WIN)
+// Flaky on Mac. http://crbug.com/517420
+#if defined(OS_MACOSX)
 #define MAYBE_LaunchTwice DISABLED_LaunchTwice
 #else
 #define MAYBE_LaunchTwice LaunchTwice
@@ -256,8 +256,8 @@
         FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
 }
 
-// Flaky on Mac and Windows. http://crbug.com/517420
-#if defined(OS_MACOSX) || defined(OS_WIN)
+// Flaky on Mac. http://crbug.com/517420
+#if defined(OS_MACOSX)
 #define MAYBE_MultipleLaunchTasks DISABLED_MultipleLaunchTasks
 #else
 #define MAYBE_MultipleLaunchTasks MultipleLaunchTasks
@@ -278,8 +278,8 @@
   EXPECT_EQ(0, launch_count);
 }
 
-// Flaky on Mac and Windows. http://crbug.com/517420
-#if defined(OS_MACOSX) || defined(OS_WIN)
+// Flaky on Mac. http://crbug.com/517420
+#if defined(OS_MACOSX)
 #define MAYBE_SameLaunchTask DISABLED_SameLaunchTask
 #else
 #define MAYBE_SameLaunchTask SameLaunchTask
@@ -300,8 +300,8 @@
 
 // Tests whether disconnecting from the service IPC causes the service process
 // to die.
-// Flaky on Mac and Windows. http://crbug.com/517420
-#if defined(OS_MACOSX) || defined(OS_WIN)
+// Flaky on Mac. http://crbug.com/517420
+#if defined(OS_MACOSX)
 #define MAYBE_DieOnDisconnect DISABLED_DieOnDisconnect
 #else
 #define MAYBE_DieOnDisconnect DieOnDisconnect
@@ -315,8 +315,8 @@
   Disconnect();
 }
 
-// Flaky on Mac and Windows. http://crbug.com/517420
-#if defined(OS_MACOSX) || defined(OS_WIN)
+// Flaky on Mac. http://crbug.com/517420
+#if defined(OS_MACOSX)
 #define MAYBE_ForceShutdown DISABLED_ForceShutdown
 #else
 #define MAYBE_ForceShutdown ForceShutdown
@@ -333,8 +333,8 @@
   ForceServiceProcessShutdown(version_info::GetVersionNumber(), service_pid);
 }
 
-// Flaky on Mac and Windows. http://crbug.com/517420
-#if defined(OS_MACOSX) || defined(OS_WIN)
+// Flaky on Mac. http://crbug.com/517420
+#if defined(OS_MACOSX)
 #define MAYBE_CheckPid DISABLED_CheckPid
 #else
 #define MAYBE_CheckPid CheckPid
@@ -360,9 +360,8 @@
       base::TimeDelta()));
 }
 
-// Histograms disabled on OSX (http://crbug.com/406227) and Windows
-// (http://crbug.com/517420).
-#if defined(OS_MACOSX) || defined(OS_WIN)
+// Histograms disabled on OSX http://crbug.com/406227
+#if defined(OS_MACOSX)
 #define MAYBE_HistogramsTimeout DISABLED_HistogramsTimeout
 #define MAYBE_Histograms DISABLED_Histograms
 #else
diff --git a/chrome/browser/sync/test/integration/configuration_refresher.cc b/chrome/browser/sync/test/integration/configuration_refresher.cc
new file mode 100644
index 0000000..6cabcc8
--- /dev/null
+++ b/chrome/browser/sync/test/integration/configuration_refresher.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/test/integration/configuration_refresher.h"
+
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_service.h"
+
+ConfigurationRefresher::ConfigurationRefresher() : scoped_observer_(this) {}
+
+ConfigurationRefresher::~ConfigurationRefresher() {}
+
+void ConfigurationRefresher::Observe(syncer::SyncService* sync_service) {
+  scoped_observer_.Add(sync_service);
+}
+
+void ConfigurationRefresher::OnSyncConfigurationCompleted(
+    syncer::SyncService* sync_service) {
+  // Only allowed to trigger refresh/schedule nudges for protocol types, things
+  // like PROXY_TABS are not allowed.
+  sync_service->TriggerRefresh(syncer::Intersection(
+      sync_service->GetActiveDataTypes(), syncer::ProtocolTypes()));
+}
diff --git a/chrome/browser/sync/test/integration/configuration_refresher.h b/chrome/browser/sync/test/integration/configuration_refresher.h
new file mode 100644
index 0000000..ae141ee
--- /dev/null
+++ b/chrome/browser/sync/test/integration/configuration_refresher.h
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_TEST_INTEGRATION_CONFIGURATION_REFRESHER_H_
+#define CHROME_BROWSER_SYNC_TEST_INTEGRATION_CONFIGURATION_REFRESHER_H_
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "components/sync/driver/sync_service_observer.h"
+
+// Triggers a GetUpdates via refresh for any observed SyncService after a
+// configuration. This class was created to be used in conjunction with fake
+// invalidations. It turns out there's a race during configuration, after the
+// initial GetUpdates was called, but before invalidations were re-subscribed to
+// that caused updates to be missed. This resulted in some flakey test cases,
+// see crbug,com/644367 for more details. This class fills the gap by forcing a
+// GetUpdates after configuration to fetch anything missed while a client was
+// not subscribed to invalidation(s).
+class ConfigurationRefresher : public syncer::SyncServiceObserver {
+ public:
+  ConfigurationRefresher();
+  ~ConfigurationRefresher() override;
+  void Observe(syncer::SyncService* sync_service);
+
+ private:
+  // syncer::SyncServiceObserver implementation.
+  void OnSyncConfigurationCompleted(syncer::SyncService* sync_service) override;
+
+  ScopedObserver<syncer::SyncService, ConfigurationRefresher> scoped_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConfigurationRefresher);
+};
+
+#endif  // CHROME_BROWSER_SYNC_TEST_INTEGRATION_CONFIGURATION_REFRESHER_H_
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 4515ef5..aecab0aa 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -217,6 +217,7 @@
     : test_type_(test_type),
       server_type_(SERVER_TYPE_UNDECIDED),
       num_clients_(-1),
+      configuration_refresher_(std::make_unique<ConfigurationRefresher>()),
       use_verifier_(true),
       create_gaia_account_at_runtime_(false) {
   sync_datatype_helper::AssociateWithTest(this);
@@ -642,6 +643,8 @@
       invalidation_service->DisableSelfNotifications();
     }
     fake_server_invalidation_services_[index] = invalidation_service;
+    configuration_refresher_->Observe(
+        ProfileSyncServiceFactory::GetForProfile(GetProfile(index)));
   } else {
     invalidation::P2PInvalidationService* p2p_invalidation_service =
         static_cast<invalidation::P2PInvalidationService*>(
@@ -774,10 +777,10 @@
     }
   }
 
+  // Delete things that unsubscribe in destructor before their targets are gone.
   invalidation_forwarders_.clear();
   sync_refreshers_.clear();
-  fake_server_invalidation_services_.clear();
-  clients_.clear();
+  configuration_refresher_.reset();
 }
 
 void SyncTest::SetUpOnMainThread() {
diff --git a/chrome/browser/sync/test/integration/sync_test.h b/chrome/browser/sync/test/integration/sync_test.h
index afd9893..898ecd5 100644
--- a/chrome/browser/sync/test/integration/sync_test.h
+++ b/chrome/browser/sync/test/integration/sync_test.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/process/process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/test/integration/configuration_refresher.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/protocol/sync_protocol_error.h"
@@ -432,6 +433,9 @@
   std::vector<fake_server::FakeServerInvalidationService*>
       fake_server_invalidation_services_;
 
+  // Triggers a GetUpdates via refresh after a configuration.
+  std::unique_ptr<ConfigurationRefresher> configuration_refresher_;
+
   // Sync profile against which changes to individual profiles are verified. We
   // don't need a corresponding verifier sync client because the contents of the
   // verifier profile are strictly local, and are not meant to be synced.
diff --git a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
index 537f07ec..1d279a3 100644
--- a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
@@ -1860,7 +1860,7 @@
   ASSERT_TRUE(IsEncryptionComplete(1));
   ASSERT_TRUE(GetSyncService(1)->IsPassphraseRequired());
 
-  // Client 1 adds bookmarks between the first two and between the second two.
+  // Client 0 adds bookmarks between the first two and between the second two.
   ASSERT_NE(nullptr, AddURL(0, 1, IndexedURLTitle(3), GURL(IndexedURL(3))));
   ASSERT_NE(nullptr, AddURL(0, 3, IndexedURLTitle(4), GURL(IndexedURL(4))));
   EXPECT_FALSE(AllModelsMatchVerifier());
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index 3b2bf76..5ff69f6e 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -290,13 +290,11 @@
   int hit_test =
       widget()->non_client_view()->NonClientHitTest(point_in_view_coords);
   if (hit_test == HTCAPTION) {
-    menu_model_adapter_.reset(new views::MenuModelAdapter(
+    menu_runner_ = std::make_unique<views::MenuRunner>(
         menu_model_.get(),
+        views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU,
         base::Bind(&ChromeNativeAppWindowViewsAuraAsh::OnMenuClosed,
-                   base::Unretained(this))));
-    menu_runner_.reset(new views::MenuRunner(
-        menu_model_adapter_->CreateMenu(),
-        views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
+                   base::Unretained(this)));
     menu_runner_->RunMenuAt(source->GetWidget(), NULL,
                             gfx::Rect(p, gfx::Size(0, 0)),
                             views::MENU_ANCHOR_TOPLEFT, source_type);
@@ -576,7 +574,6 @@
 
 void ChromeNativeAppWindowViewsAuraAsh::OnMenuClosed() {
   menu_runner_.reset();
-  menu_model_adapter_.reset();
   menu_model_.reset();
 }
 
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h
index be3e2ca7..18af222c 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h
@@ -25,7 +25,6 @@
 }
 
 namespace views {
-class MenuModelAdapter;
 class MenuRunner;
 }
 
@@ -130,7 +129,7 @@
   FRIEND_TEST_ALL_PREFIXES(ShapedAppWindowTargeterTest,
                            ResizeInsetsWithinBounds);
 
-  // Callback for MenuModelAdapter
+  // Callback for MenuRunner
   void OnMenuClosed();
 
   // Helper function which returns true if in tablet mode, the auto hide
@@ -146,7 +145,6 @@
 
   // Used to show the system menu.
   std::unique_ptr<ui::MenuModel> menu_model_;
-  std::unique_ptr<views::MenuModelAdapter> menu_model_adapter_;
   std::unique_ptr<views::MenuRunner> menu_runner_;
 
   // Used for displaying the toast with instructions on exiting fullscreen.
diff --git a/chrome/browser/ui/views/frame/browser_frame.h b/chrome/browser/ui/views/frame/browser_frame.h
index 09d6302..188f732d 100644
--- a/chrome/browser/ui/views/frame/browser_frame.h
+++ b/chrome/browser/ui/views/frame/browser_frame.h
@@ -121,7 +121,7 @@
   ui::MenuModel* GetSystemMenuModel();
 
  private:
-  // Callback for MenuModelAdapter.
+  // Callback for MenuRunner.
   void OnMenuClosed();
 
   NativeBrowserFrame* native_browser_frame_;
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index 3b3dc141..9e52afd 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -83,7 +83,7 @@
   UMA_HISTOGRAM_BOOLEAN("Printing.CUPS.IppAttributesSuccess", success);
 }
 
-// Parsees |printer_uri| into its components and written into |uri|.  Returns
+// Parses |printer_uri| into its components and written into |uri|.  Returns
 // true if the uri was parsed successfully, returns false otherwise.  No changes
 // are made to |uri| if this function returns false.
 bool ParseUri(const std::string& printer_uri, PrinterUri* uri) {
@@ -181,9 +181,9 @@
   return host;
 }
 
-// Returns a JSON representation of |printer| as a CupsPrinterInfo. Note it's
-// possible that this function returns a nullptr if the printer url is not in
-// the right format.
+// Returns a JSON representation of |printer| as a CupsPrinterInfo. If the
+// printer uri cannot be parsed, the relevant fields are populated with default
+// values.
 std::unique_ptr<base::DictionaryValue> GetPrinterInfo(const Printer& printer) {
   std::unique_ptr<base::DictionaryValue> printer_info =
       CreateEmptyPrinterInfo();
@@ -196,7 +196,13 @@
 
   PrinterUri uri;
   if (!ParseUri(printer.uri(), &uri)) {
-    return nullptr;
+    // Uri is invalid so we set default values.
+    LOG(WARNING) << "Could not parse uri.  Defaulting values";
+    printer_info->SetString("printerAddress", "");
+    printer_info->SetString("printerQueue", "");
+    printer_info->SetString("printerProtocol",
+                            "ipp");  // IPP is our default protocol.
+    return printer_info;
   }
 
   if (base::ToLowerASCII(uri.scheme) == "usb") {
@@ -363,11 +369,9 @@
 
   auto printers_list = base::MakeUnique<base::ListValue>();
   for (const Printer& printer : printers) {
-    // TODO(skau): Theoretically |printer_info| should not be a nullptr as we
-    // should not allow adding an invalid configured printer to PrinterManager.
-    auto printer_info = GetPrinterInfo(printer);
-    if (printer_info)
-      printers_list->Append(std::move(printer_info));
+    // Some of these printers could be invalid but we want to allow the user
+    // to edit them. crbug.com/778383
+    printers_list->Append(GetPrinterInfo(printer));
   }
 
   auto response = base::MakeUnique<base::DictionaryValue>();
@@ -816,14 +820,10 @@
   std::unique_ptr<base::ListValue> printers_list =
       base::MakeUnique<base::ListValue>();
   for (const Printer& printer : automatic_printers_) {
-    auto printer_info = GetPrinterInfo(printer);
-    if (printer_info)
-      printers_list->Append(std::move(printer_info));
+    printers_list->Append(GetPrinterInfo(printer));
   }
   for (const Printer& printer : discovered_printers_) {
-    auto printer_info = GetPrinterInfo(printer);
-    if (printer_info)
-      printers_list->Append(std::move(printer_info));
+    printers_list->Append(GetPrinterInfo(printer));
   }
 
   FireWebUIListener("on-printer-discovered", *printers_list);
diff --git a/chrome/browser/vr/service/DEPS b/chrome/browser/vr/service/DEPS
index 1d7233d..ac9c5de 100644
--- a/chrome/browser/vr/service/DEPS
+++ b/chrome/browser/vr/service/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+device/vr",
+  "+content/public/common/service_manager_connection.h",
 ]
diff --git a/chrome/browser/vr/service/vr_device_manager.cc b/chrome/browser/vr/service/vr_device_manager.cc
index 1fcc1ba..5b3fd890 100644
--- a/chrome/browser/vr/service/vr_device_manager.cc
+++ b/chrome/browser/vr/service/vr_device_manager.cc
@@ -11,11 +11,13 @@
 #include "base/memory/singleton.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_features.h"
+#include "content/public/common/service_manager_connection.h"
 #include "device/vr/features/features.h"
 #include "device/vr/vr_device_provider.h"
 
 #if defined(OS_ANDROID)
 #include "device/vr/android/gvr/gvr_device_provider.h"
+#include "device/vr/orientation/orientation_device_provider.h"
 #endif
 
 #if BUILDFLAG(ENABLE_OPENVR)
@@ -26,14 +28,23 @@
 
 namespace {
 VRDeviceManager* g_vr_device_manager = nullptr;
-}
+}  // namespace
 
 VRDeviceManager* VRDeviceManager::GetInstance() {
   if (!g_vr_device_manager) {
     // Register VRDeviceProviders for the current platform
     ProviderList providers;
+
 #if defined(OS_ANDROID)
     providers.emplace_back(std::make_unique<device::GvrDeviceProvider>());
+
+    content::ServiceManagerConnection* connection =
+        content::ServiceManagerConnection::GetForProcess();
+    if (connection) {
+      providers.emplace_back(
+          std::make_unique<device::VROrientationDeviceProvider>(
+              connection->GetConnector()));
+    }
 #endif
 
 #if BUILDFLAG(ENABLE_OPENVR)
@@ -51,6 +62,7 @@
 
 VRDeviceManager::VRDeviceManager(ProviderList providers)
     : providers_(std::move(providers)) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   CHECK(!g_vr_device_manager);
   g_vr_device_manager = this;
 }
@@ -68,8 +80,10 @@
   // when they are created.
   InitializeProviders();
 
-  for (const DeviceMap::value_type& map_entry : devices_)
-    service->ConnectDevice(map_entry.second);
+  for (const DeviceMap::value_type& map_entry : devices_) {
+    if (!map_entry.second->IsFallbackDevice() || devices_.size() == 1)
+      service->ConnectDevice(map_entry.second);
+  }
 
   if (AreAllProvidersInitialized())
     service->InitializationComplete();
@@ -95,9 +109,21 @@
   if (device->GetId() == device::VR_DEVICE_LAST_ID)
     return;
 
+  // If we were previously using a fallback device, remove it.
+  // TODO(offenwanger): This has the potential to cause device change events to
+  // fire in rapid succession. This should be discussed and resolved when we
+  // start to actually add and remove devices.
+  if (devices_.size() == 1 && devices_.begin()->second->IsFallbackDevice()) {
+    device::VRDevice* device = devices_.begin()->second;
+    for (VRServiceImpl* service : services_)
+      service->RemoveDevice(device);
+  }
+
   devices_[device->GetId()] = device;
-  for (VRServiceImpl* service : services_)
-    service->ConnectDevice(device);
+  if (!device->IsFallbackDevice() || devices_.size() == 1) {
+    for (VRServiceImpl* service : services_)
+      service->ConnectDevice(device);
+  }
 }
 
 void VRDeviceManager::RemoveDevice(device::VRDevice* device) {
@@ -111,6 +137,12 @@
     service->RemoveDevice(device);
 
   devices_.erase(it);
+
+  if (devices_.size() == 1 && devices_.begin()->second->IsFallbackDevice()) {
+    device::VRDevice* device = devices_.begin()->second;
+    for (VRServiceImpl* service : services_)
+      service->ConnectDevice(device);
+  }
 }
 
 device::VRDevice* VRDeviceManager::GetDevice(unsigned int index) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 37552a9..fcc9d0e 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4892,6 +4892,8 @@
       "../browser/sync/test/integration/await_match_status_change_checker.h",
       "../browser/sync/test/integration/bookmarks_helper.cc",
       "../browser/sync/test/integration/bookmarks_helper.h",
+      "../browser/sync/test/integration/configuration_refresher.cc",
+      "../browser/sync/test/integration/configuration_refresher.h",
       "../browser/sync/test/integration/dictionary_helper.cc",
       "../browser/sync/test/integration/dictionary_helper.h",
       "../browser/sync/test/integration/dictionary_load_observer.cc",
diff --git a/chrome/test/data/extensions/context_menus/checkboxes/manifest.json b/chrome/test/data/extensions/context_menus/checkboxes/manifest.json
new file mode 100644
index 0000000..b99daf45
--- /dev/null
+++ b/chrome/test/data/extensions/context_menus/checkboxes/manifest.json
@@ -0,0 +1,11 @@
+{
+  "name" : "Context Menus Test Extension",
+  "description": "Tests context menu checkbox update",
+  "version" : "0.1",
+  "manifest_version": 2,
+  "permissions": [ "contextMenus"],
+  "background": {
+    "scripts": ["test.js"]
+  }
+}
+
diff --git a/chrome/test/data/extensions/context_menus/checkboxes/test.js b/chrome/test/data/extensions/context_menus/checkboxes/test.js
new file mode 100644
index 0000000..4d626fa
--- /dev/null
+++ b/chrome/test/data/extensions/context_menus/checkboxes/test.js
@@ -0,0 +1,60 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+window.onload = function() {
+  createFirstCheckbox()
+      .then(createSecondCheckbox)
+      .then(checkSecondCheckbox)
+      .then(createNormalMenuItem)
+      .then(function() {
+        chrome.test.sendMessage('Menu created');
+      });
+};
+
+function createFirstCheckbox() {
+  return new Promise(function(resolve, reject) {
+    chrome.contextMenus.create({
+      id: 'checkbox1',
+      type: 'checkbox',
+      title: 'Checkbox 1',
+      onclick: function() {
+        chrome.test.sendMessage('onclick checkbox 1');
+      }
+    }, resolve);
+  });
+}
+
+function createSecondCheckbox() {
+  return new Promise(function(resolve, reject) {
+    chrome.contextMenus.create({
+      id: 'checkbox2',
+      type: 'checkbox',
+      title: 'Checkbox 2',
+      onclick: function() {
+        chrome.test.sendMessage('onclick checkbox 2');
+      }
+    }, resolve);
+  });
+}
+
+function checkSecondCheckbox() {
+  return new Promise(function(resolve, reject) {
+    chrome.contextMenus.update('checkbox2', {checked: true}, resolve);
+  });
+}
+
+function createNormalMenuItem() {
+  return new Promise(function(resolve, reject) {
+    chrome.contextMenus.create({
+      id: 'item1',
+      title: 'Item 1',
+      onclick: function() {
+        chrome.test.sendMessage('onclick normal item');
+        chrome.contextMenus.update('checkbox2', {checked: false}, function() {
+          chrome.test.sendMessage('checkbox2 unchecked');
+        });
+      }
+    }, resolve);
+  });
+}
diff --git a/chromeos/dbus/fake_image_loader_client.cc b/chromeos/dbus/fake_image_loader_client.cc
index 9dcef0b..8ed20de 100644
--- a/chromeos/dbus/fake_image_loader_client.cc
+++ b/chromeos/dbus/fake_image_loader_client.cc
@@ -41,4 +41,10 @@
   std::move(callback).Run(base::nullopt);
 }
 
+void FakeImageLoaderClient::UnmountComponent(
+    const std::string& name,
+    DBusMethodCallback<bool> callback) {
+  std::move(callback).Run(base::nullopt);
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/fake_image_loader_client.h b/chromeos/dbus/fake_image_loader_client.h
index 24898b8..c2cc00f 100644
--- a/chromeos/dbus/fake_image_loader_client.h
+++ b/chromeos/dbus/fake_image_loader_client.h
@@ -36,6 +36,8 @@
   void RequestComponentVersion(
       const std::string& name,
       DBusMethodCallback<std::string> callback) override;
+  void UnmountComponent(const std::string& name,
+                        DBusMethodCallback<bool> callback) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FakeImageLoaderClient);
diff --git a/chromeos/dbus/image_loader_client.cc b/chromeos/dbus/image_loader_client.cc
index bad965a..a23a300 100644
--- a/chromeos/dbus/image_loader_client.cc
+++ b/chromeos/dbus/image_loader_client.cc
@@ -87,6 +87,17 @@
                                       std::move(callback)));
   }
 
+  void UnmountComponent(const std::string& name,
+                        DBusMethodCallback<bool> callback) override {
+    dbus::MethodCall method_call(imageloader::kImageLoaderServiceInterface,
+                                 imageloader::kUnmountComponent);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendString(name);
+    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+                       base::BindOnce(&ImageLoaderClientImpl::OnBoolMethod,
+                                      std::move(callback)));
+  }
+
  protected:
   // DBusClient override.
   void Init(dbus::Bus* bus) override {
diff --git a/chromeos/dbus/image_loader_client.h b/chromeos/dbus/image_loader_client.h
index 8880ccee..57210bf0 100644
--- a/chromeos/dbus/image_loader_client.h
+++ b/chromeos/dbus/image_loader_client.h
@@ -50,6 +50,10 @@
   virtual void RemoveComponent(const std::string& name,
                                DBusMethodCallback<bool> callback) = 0;
 
+  // Unmounts all mount points given component |name|.
+  virtual void UnmountComponent(const std::string& name,
+                                DBusMethodCallback<bool> callback) = 0;
+
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
   static ImageLoaderClient* Create();
diff --git a/chromeos/dbus/services/component_updater_service_provider.cc b/chromeos/dbus/services/component_updater_service_provider.cc
index ac95151..8fe15e5 100644
--- a/chromeos/dbus/services/component_updater_service_provider.cc
+++ b/chromeos/dbus/services/component_updater_service_provider.cc
@@ -33,6 +33,14 @@
                  weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&ComponentUpdaterServiceProvider::OnExported,
                  weak_ptr_factory_.GetWeakPtr()));
+
+  exported_object->ExportMethod(
+      kComponentUpdaterServiceInterface,
+      kComponentUpdaterServiceUnloadComponentMethod,
+      base::Bind(&ComponentUpdaterServiceProvider::UnloadComponent,
+                 weak_ptr_factory_.GetWeakPtr()),
+      base::Bind(&ComponentUpdaterServiceProvider::OnExported,
+                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ComponentUpdaterServiceProvider::OnExported(
@@ -82,4 +90,23 @@
   }
 }
 
+void ComponentUpdaterServiceProvider::UnloadComponent(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+  std::string component_name;
+  if (reader.PopString(&component_name)) {
+    if (delegate_->UnloadComponent(component_name)) {
+      response_sender.Run(dbus::Response::FromMethodCall(method_call));
+    } else {
+      response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+          method_call, kErrorInternalError, "Failed to unload component"));
+    }
+  } else {
+    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kErrorInvalidArgs,
+        "Missing component name string argument."));
+  }
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/services/component_updater_service_provider.h b/chromeos/dbus/services/component_updater_service_provider.h
index 7ccb088a..2aacd281 100644
--- a/chromeos/dbus/services/component_updater_service_provider.h
+++ b/chromeos/dbus/services/component_updater_service_provider.h
@@ -22,19 +22,25 @@
 
 namespace chromeos {
 
-// This class exports a "LoadComponent" D-Bus method that installs a component
-// and return the installed path (if successful) or an error message
-// (on failure):
+// This class exports D-Bus methods that manage components:
 //
+// LoadComponent:
 // % dbus-send --system --type=method_call --print-reply
 //     --dest=org.chromium.ComponentUpdaterService
 //     /org/chromium/ComponentUpdaterService
 //     org.chromium.ComponentUpdaterService.LoadComponent
 //     "string:|component name|"
 //
-// -> method return sender=:1.42 -> destination=:1.43 reply_serial=2
+// % string "/run/imageloader/|component name|/|version|"
 //
-// string "/run/imageloader/|component name|/|version|"
+// UnloadComponent:
+// % dbus-send --system --type=method_call --print-reply
+//     --dest=org.chromium.ComponentUpdaterService
+//     /org/chromium/ComponentUpdaterService
+//     org.chromium.ComponentUpdaterService.UnloadComponent
+//     "string:|component name|"
+//
+// % (returns empty response on success and error response on failure)
 class CHROMEOS_EXPORT ComponentUpdaterServiceProvider
     : public CrosDBusService::ServiceProviderInterface {
  public:
@@ -47,7 +53,9 @@
 
     virtual void LoadComponent(
         const std::string& name,
-        const base::Callback<void(const std::string&)>& load_callback) = 0;
+        base::OnceCallback<void(const std::string&)> load_callback) = 0;
+
+    virtual bool UnloadComponent(const std::string& name) = 0;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(Delegate);
@@ -75,6 +83,10 @@
                        dbus::ExportedObject::ResponseSender response_sender,
                        const std::string& result);
 
+  // Called on UI thread in response to a D-Bus request.
+  void UnloadComponent(dbus::MethodCall* method_call,
+                       dbus::ExportedObject::ResponseSender response_sender);
+
   std::unique_ptr<Delegate> delegate_;
   // Keep this last so that all weak pointers will be invalidated at the
   // beginning of destruction.
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc
index e20b03de..f0118990 100644
--- a/components/autofill/core/browser/autofill_experiments.cc
+++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -65,6 +65,8 @@
     "AutofillUpstreamAllowAllEmailDomains", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillUpstreamRequestCvcIfMissing{
     "AutofillUpstreamRequestCvcIfMissing", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillUpstreamSendPanFirstSix{
+    "AutofillUpstreamSendPanFirstSix", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillUpstreamShowGoogleLogo{
     "AutofillUpstreamShowGoogleLogo", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillUpstreamShowNewUi{
@@ -293,6 +295,10 @@
 #endif
 }
 
+bool IsAutofillUpstreamSendPanFirstSixExperimentEnabled() {
+  return base::FeatureList::IsEnabled(kAutofillUpstreamSendPanFirstSix);
+}
+
 bool IsAutofillUpstreamShowGoogleLogoExperimentEnabled() {
 #if defined(OS_ANDROID)
   return false;
diff --git a/components/autofill/core/browser/autofill_experiments.h b/components/autofill/core/browser/autofill_experiments.h
index 6221cf2..c6d770c8 100644
--- a/components/autofill/core/browser/autofill_experiments.h
+++ b/components/autofill/core/browser/autofill_experiments.h
@@ -44,6 +44,7 @@
 extern const base::Feature kAutofillToolkitViewsCreditCardDialogsMac;
 extern const base::Feature kAutofillUpstreamAllowAllEmailDomains;
 extern const base::Feature kAutofillUpstreamRequestCvcIfMissing;
+extern const base::Feature kAutofillUpstreamSendPanFirstSix;
 extern const base::Feature kAutofillUpstreamShowGoogleLogo;
 extern const base::Feature kAutofillUpstreamShowNewUi;
 extern const base::Feature kAutofillUpstreamUseAutofillProfileComparator;
@@ -141,6 +142,11 @@
 // in the offer to save bubble if it was not detected during the checkout flow.
 bool IsAutofillUpstreamRequestCvcIfMissingExperimentEnabled();
 
+// Returns whether the experiment is enabled where Chrome Upstream sends the
+// first six digits of the card PAN to Google Payments to help determine whether
+// card upload is possible.
+bool IsAutofillUpstreamSendPanFirstSixExperimentEnabled();
+
 // Returns whether the experiment is enabled where Chrome Upstream displays a
 // Google Logo in the save card bubble/infobar.
 bool IsAutofillUpstreamShowGoogleLogoExperimentEnabled();
diff --git a/components/autofill/core/browser/credit_card_save_manager.cc b/components/autofill/core/browser/credit_card_save_manager.cc
index 2c54aec..16708ef 100644
--- a/components/autofill/core/browser/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/credit_card_save_manager.cc
@@ -159,6 +159,10 @@
     return;
   }
 
+  if (IsAutofillUpstreamSendPanFirstSixExperimentEnabled()) {
+    upload_request_.active_experiments.push_back(
+        kAutofillUpstreamSendPanFirstSix.name);
+  }
   if (IsAutofillUpstreamShowNewUiExperimentEnabled()) {
     upload_request_.active_experiments.push_back(
         kAutofillUpstreamShowNewUi.name);
@@ -169,9 +173,11 @@
   }
 
   // All required data is available, start the upload process.
-  payments_client_->GetUploadDetails(upload_request_.profiles,
-                                     upload_request_.active_experiments,
-                                     app_locale_);
+  payments_client_->GetUploadDetails(
+      upload_request_.profiles,
+      base::UTF16ToASCII(CreditCard::StripSeparators(card.number()))
+          .substr(0, 6),
+      upload_request_.active_experiments, app_locale_);
 }
 
 bool CreditCardSaveManager::IsCreditCardUploadEnabled() {
diff --git a/components/autofill/core/browser/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
index abfbf1f..d4e5b14 100644
--- a/components/autofill/core/browser/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
@@ -105,8 +105,10 @@
   ~TestPaymentsClient() override {}
 
   void GetUploadDetails(const std::vector<AutofillProfile>& addresses,
+                        const std::string& pan_first_six,
                         const std::vector<const char*>& active_experiments,
                         const std::string& app_locale) override {
+    pan_first_six_ = pan_first_six;
     active_experiments_ = active_experiments;
     save_delegate_->OnDidGetUploadDetails(
         app_locale == "en-US" ? AutofillClient::SUCCESS
@@ -122,6 +124,7 @@
   }
 
   std::string server_id_;
+  std::string pan_first_six_;
   std::vector<const char*> active_experiments_;
 
   void SetSaveDelegate(payments::PaymentsClientSaveDelegate* save_delegate) {
@@ -343,6 +346,10 @@
 
   bool credit_card_was_uploaded() { return credit_card_was_uploaded_; }
 
+  const std::string GetPanFirstSix() const {
+    return test_payments_client_->pan_first_six_;
+  }
+
   const std::vector<const char*>& GetActiveExperiments() const {
     return test_payments_client_->active_experiments_;
   }
@@ -414,6 +421,10 @@
         kAutofillUpstreamRequestCvcIfMissing);
   }
 
+  void EnableAutofillUpstreamSendPanFirstSixExperiment() {
+    scoped_feature_list_.InitAndEnableFeature(kAutofillUpstreamSendPanFirstSix);
+  }
+
   void EnableAutofillUpstreamShowGoogleLogoExperiment() {
     scoped_feature_list_.InitAndEnableFeature(kAutofillUpstreamShowGoogleLogo);
   }
@@ -1421,38 +1432,6 @@
 }
 
 TEST_F(CreditCardSaveManagerTest,
-       UploadCreditCard_DoNotAddNewUiFlagStateToRequestIfExperimentOff) {
-  personal_data_.ClearProfiles();
-  credit_card_save_manager_->set_credit_card_upload_enabled(true);
-
-  // Create, fill and submit an address form in order to establish a recent
-  // profile which can be selected for the upload request.
-  FormData address_form;
-  test::CreateTestAddressFormData(&address_form);
-  FormsSeen(std::vector<FormData>(1, address_form));
-  ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
-  FormSubmitted(address_form);
-
-  // Set up our credit card form data.
-  FormData credit_card_form;
-  CreateTestCreditCardFormData(&credit_card_form, true, false);
-  FormsSeen(std::vector<FormData>(1, credit_card_form));
-
-  // Edit the data, and submit.
-  credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
-  credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
-  credit_card_form.fields[2].value = ASCIIToUTF16("11");
-  credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
-  credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
-  // Confirm upload happened and the new UI flag was not sent in the request.
-  EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
-  FormSubmitted(credit_card_form);
-  EXPECT_TRUE(credit_card_save_manager_->credit_card_was_uploaded());
-  EXPECT_TRUE(credit_card_save_manager_->GetActiveExperiments().empty());
-}
-
-TEST_F(CreditCardSaveManagerTest,
        UploadCreditCard_AddShowGoogleLogoFlagStateToRequestIfExperimentOn) {
   EnableAutofillUpstreamShowGoogleLogoExperiment();
   personal_data_.ClearProfiles();
@@ -1486,39 +1465,6 @@
   EXPECT_THAT(credit_card_save_manager_->GetActiveExperiments(),
               UnorderedElementsAre(kAutofillUpstreamShowGoogleLogo.name));
 }
-
-TEST_F(CreditCardSaveManagerTest,
-       UploadCreditCard_DoNotAddShowGoogleLogoFlagStateToRequestIfExpOff) {
-  personal_data_.ClearProfiles();
-  credit_card_save_manager_->set_credit_card_upload_enabled(true);
-
-  // Create, fill and submit an address form in order to establish a recent
-  // profile which can be selected for the upload request.
-  FormData address_form;
-  test::CreateTestAddressFormData(&address_form);
-  FormsSeen(std::vector<FormData>(1, address_form));
-  ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
-  FormSubmitted(address_form);
-
-  // Set up our credit card form data.
-  FormData credit_card_form;
-  CreateTestCreditCardFormData(&credit_card_form, true, false);
-  FormsSeen(std::vector<FormData>(1, credit_card_form));
-
-  // Edit the data, and submit.
-  credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
-  credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
-  credit_card_form.fields[2].value = ASCIIToUTF16("11");
-  credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
-  credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
-  // Confirm upload happened and the show Google logo flag was not sent in the
-  // request.
-  EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
-  FormSubmitted(credit_card_form);
-  EXPECT_TRUE(credit_card_save_manager_->credit_card_was_uploaded());
-  EXPECT_TRUE(credit_card_save_manager_->GetActiveExperiments().empty());
-}
 #endif
 
 TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoProfileAvailable) {
@@ -2521,4 +2467,74 @@
   EXPECT_FALSE(credit_card_save_manager_->credit_card_was_uploaded());
 }
 
+TEST_F(CreditCardSaveManagerTest,
+       UploadCreditCard_DoNotAddAnyFlagStatesToRequestIfExperimentsOff) {
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->set_credit_card_upload_enabled(true);
+
+  // Create, fill and submit an address form in order to establish a recent
+  // profile which can be selected for the upload request.
+  FormData address_form;
+  test::CreateTestAddressFormData(&address_form);
+  FormsSeen(std::vector<FormData>(1, address_form));
+  ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+  FormSubmitted(address_form);
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+  credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+  credit_card_form.fields[2].value = ASCIIToUTF16("11");
+  credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+  credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+  // Confirm upload happened and that no experiment flag state was sent in the
+  // request.
+  EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(credit_card_save_manager_->credit_card_was_uploaded());
+  EXPECT_TRUE(credit_card_save_manager_->GetActiveExperiments().empty());
+}
+
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_AddPanFirstSixToRequest) {
+  EnableAutofillUpstreamSendPanFirstSixExperiment();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->set_credit_card_upload_enabled(true);
+
+  // Create, fill and submit an address form in order to establish a recent
+  // profile which can be selected for the upload request.
+  FormData address_form;
+  test::CreateTestAddressFormData(&address_form);
+  FormsSeen(std::vector<FormData>(1, address_form));
+  ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+  FormSubmitted(address_form);
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+  credit_card_form.fields[1].value = ASCIIToUTF16("4444333322221111");
+  credit_card_form.fields[2].value = ASCIIToUTF16("11");
+  credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+  credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+  // Confirm that the first six digits of the credit card number were included
+  // in the request.
+  EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(credit_card_save_manager_->credit_card_was_uploaded());
+  EXPECT_EQ(credit_card_save_manager_->GetPanFirstSix(), "444433");
+  // Confirm that the "send pan first six" experiment flag was sent in the
+  // request.
+  EXPECT_THAT(credit_card_save_manager_->GetActiveExperiments(),
+              UnorderedElementsAre(kAutofillUpstreamSendPanFirstSix.name));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index bae77b9..d2440a8b 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -261,10 +261,12 @@
 class GetUploadDetailsRequest : public PaymentsRequest {
  public:
   GetUploadDetailsRequest(const std::vector<AutofillProfile>& addresses,
+                          const std::string& pan_first_six,
                           const std::vector<const char*>& active_experiments,
                           const std::string& app_locale,
                           PaymentsClientSaveDelegate* delegate)
       : addresses_(addresses),
+        pan_first_six_(pan_first_six),
         active_experiments_(active_experiments),
         app_locale_(app_locale),
         delegate_(delegate) {}
@@ -294,6 +296,10 @@
     }
     request_dict.Set("address", std::move(addresses));
 
+    if (IsAutofillUpstreamSendPanFirstSixExperimentEnabled() &&
+        !pan_first_six_.empty())
+      request_dict.SetString("pan_first6", pan_first_six_);
+
     SetActiveExperiments(active_experiments_, &request_dict);
 
     std::string request_content;
@@ -320,6 +326,7 @@
 
  private:
   const std::vector<AutofillProfile> addresses_;
+  const std::string pan_first_six_;
   const std::vector<const char*> active_experiments_;
   std::string app_locale_;
   PaymentsClientSaveDelegate* delegate_;
@@ -477,11 +484,13 @@
 
 void PaymentsClient::GetUploadDetails(
     const std::vector<AutofillProfile>& addresses,
+    const std::string& pan_first_six,
     const std::vector<const char*>& active_experiments,
     const std::string& app_locale) {
   DCHECK(save_delegate_);
   IssueRequest(std::make_unique<GetUploadDetailsRequest>(
-                   addresses, active_experiments, app_locale, save_delegate_),
+                   addresses, pan_first_six, active_experiments, app_locale,
+                   save_delegate_),
                false);
 }
 
diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h
index ea083af..b48a7674 100644
--- a/components/autofill/core/browser/payments/payments_client.h
+++ b/components/autofill/core/browser/payments/payments_client.h
@@ -129,12 +129,14 @@
 
   // Determine if the user meets the Payments service's conditions for upload.
   // The service uses |addresses| (from which names and phone numbers are
-  // removed) and |app_locale| to determine which legal message to display. If
-  // the conditions are met, the legal message will be returned via
-  // OnDidGetUploadDetails. |active_experiments| is used by payments server to
-  // track requests that were triggered by enabled features.
+  // removed) and |app_locale| to determine which legal message to display.
+  // |pan_first_six| is the first six digits of the number of the credit card
+  // being considered for upload. If the conditions are met, the legal message
+  // will be returned via OnDidGetUploadDetails. |active_experiments| is used by
+  // Payments server to track requests that were triggered by enabled features.
   virtual void GetUploadDetails(
       const std::vector<AutofillProfile>& addresses,
+      const std::string& pan_first_six,
       const std::vector<const char*>& active_experiments,
       const std::string& app_locale);
 
diff --git a/components/autofill/core/browser/payments/payments_client_unittest.cc b/components/autofill/core/browser/payments/payments_client_unittest.cc
index 0820d33e..f1751c48 100644
--- a/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -61,6 +61,10 @@
         kAutofillSendBillingCustomerNumber);
   }
 
+  void EnableAutofillUpstreamSendPanFirstSixExperiment() {
+    scoped_feature_list_.InitAndEnableFeature(kAutofillUpstreamSendPanFirstSix);
+  }
+
   // PaymentsClientUnmaskDelegate:
   void OnDidGetRealPan(AutofillClient::PaymentsRpcResult result,
                        const std::string& real_pan) override {
@@ -99,8 +103,8 @@
   void StartGettingUploadDetails() {
     token_service_->AddAccount("example@gmail.com");
     identity_provider_->LogIn("example@gmail.com");
-    client_->GetUploadDetails(BuildTestProfiles(), std::vector<const char*>(),
-                              "language-LOCALE");
+    client_->GetUploadDetails(BuildTestProfiles(), /*pan_first_six=*/"411111",
+                              std::vector<const char*>(), "language-LOCALE");
   }
 
   void StartUploading(bool include_cvc) {
@@ -264,6 +268,26 @@
   EXPECT_TRUE(GetUploadData().find("0090") == std::string::npos);
 }
 
+TEST_F(PaymentsClientTest,
+       GetDetailsIncludesPanFirstSixInRequestIfExperimentOn) {
+  EnableAutofillUpstreamSendPanFirstSixExperiment();
+
+  StartGettingUploadDetails();
+
+  // Verify that the value of pan_first_six was included in the request.
+  EXPECT_TRUE(GetUploadData().find("\"pan_first6\":\"411111\"") !=
+              std::string::npos);
+}
+
+TEST_F(PaymentsClientTest,
+       GetDetailsDoesNotIncludePanFirstSixInRequestIfExperimentOff) {
+  StartGettingUploadDetails();
+
+  // Verify that the value of pan_first_six was left out of the request.
+  EXPECT_TRUE(GetUploadData().find("\"pan_first6\":\"411111\"") ==
+              std::string::npos);
+}
+
 TEST_F(PaymentsClientTest, UploadSuccessWithoutServerId) {
   StartUploading(/*include_cvc=*/true);
   IssueOAuthToken();
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index d2ce9a1d..9d025c1 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -163,11 +163,7 @@
 
   secure_proxy_checker_.reset(
       new SecureProxyChecker(basic_url_request_context_getter));
-  warmup_url_fetcher_.reset(new WarmupURLFetcher(
-      url_request_context_getter,
-      base::BindRepeating(
-          &DataReductionProxyConfig::HandleWarmupFetcherResponse,
-          base::Unretained(this))));
+  warmup_url_fetcher_.reset(new WarmupURLFetcher(url_request_context_getter));
 
   if (ShouldAddDefaultProxyBypassRules())
     AddDefaultProxyBypassRules();
@@ -182,7 +178,6 @@
 void DataReductionProxyConfig::OnNewClientConfigFetched() {
   DCHECK(thread_checker_.CalledOnValidThread());
   ReloadConfig();
-  FetchWarmupURL();
 }
 
 void DataReductionProxyConfig::ReloadConfig() {
@@ -206,26 +201,6 @@
   return IsDataReductionProxy(request->proxy_server(), proxy_info);
 }
 
-bool DataReductionProxyConfig::IsDataReductionProxyServerCore(
-    const net::ProxyServer& proxy_server) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsDataReductionProxy(proxy_server, nullptr /* proxy_info */));
-
-  const net::HostPortPair& host_port_pair = proxy_server.host_port_pair();
-
-  const std::vector<DataReductionProxyServer>& data_reduction_proxy_servers =
-      config_values_->proxies_for_http();
-
-  const auto proxy_it = std::find_if(
-      data_reduction_proxy_servers.begin(), data_reduction_proxy_servers.end(),
-      [&host_port_pair](const DataReductionProxyServer& proxy) {
-        return proxy.proxy_server().is_valid() &&
-               proxy.proxy_server().host_port_pair().Equals(host_port_pair);
-      });
-
-  return proxy_it->IsCoreProxy();
-}
-
 bool DataReductionProxyConfig::IsDataReductionProxy(
     const net::ProxyServer& proxy_server,
     DataReductionProxyTypeInfo* proxy_info) const {
@@ -438,56 +413,6 @@
   network_properties_manager_ = manager;
 }
 
-void DataReductionProxyConfig::HandleWarmupFetcherResponse(
-    const net::ProxyServer& proxy_server,
-    bool success_response) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // Check the proxy server used, or disable all data saver proxies?
-  if (!IsDataReductionProxy(proxy_server, nullptr)) {
-    // No need to do anything here.
-    return;
-  }
-
-  bool is_secure_drp_proxy = proxy_server.is_https() || proxy_server.is_quic();
-  bool is_core_proxy = IsDataReductionProxyServerCore(proxy_server);
-  if (is_secure_drp_proxy && is_core_proxy) {
-    UMA_HISTOGRAM_BOOLEAN(
-        "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-        "SecureProxy.Core",
-        success_response);
-  } else if (is_secure_drp_proxy && !is_core_proxy) {
-    UMA_HISTOGRAM_BOOLEAN(
-        "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-        "SecureProxy.NonCore",
-        success_response);
-  } else if (!is_secure_drp_proxy && is_core_proxy) {
-    UMA_HISTOGRAM_BOOLEAN(
-        "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-        "InsecureProxy.Core",
-        success_response);
-  } else {
-    UMA_HISTOGRAM_BOOLEAN(
-        "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-        "InsecureProxy.NonCore",
-        success_response);
-  }
-
-  bool warmup_url_failed_past =
-      network_properties_manager_->HasWarmupURLProbeFailed(is_secure_drp_proxy,
-                                                           is_core_proxy);
-
-  network_properties_manager_->SetHasWarmupURLProbeFailed(
-      is_secure_drp_proxy, is_core_proxy,
-      !success_response /* warmup failed */);
-
-  if (warmup_url_failed_past !=
-      network_properties_manager_->HasWarmupURLProbeFailed(is_secure_drp_proxy,
-                                                           is_core_proxy)) {
-    ReloadConfig();
-  }
-}
-
 void DataReductionProxyConfig::HandleSecureProxyCheckResponse(
     const std::string& response,
     const net::URLRequestStatus& status,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index f9e518f..f176edd 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -216,13 +216,6 @@
   // Returns the ID of the current network by calling the platform APIs.
   virtual std::string GetCurrentNetworkID() const;
 
-  // Callback that is executed when the warmup URL fetch is complete.
-  // |proxy_server| is the proxy server over which the warmup URL was fetched.
-  // |success_response| is true if the fetching of the URL was successful or
-  // not.
-  void HandleWarmupFetcherResponse(const net::ProxyServer& proxy_server,
-                                   bool success_response);
-
  private:
   friend class MockDataReductionProxyConfig;
   friend class TestDataReductionProxyConfig;
@@ -297,12 +290,6 @@
   // Fetches the warmup URL.
   void FetchWarmupURL();
 
-  // Returns true if |proxy_server| is a core data reduction proxy server.
-  // Should be called only if |proxy_server| is a valid data reduction proxy
-  // server.
-  bool IsDataReductionProxyServerCore(
-      const net::ProxyServer& proxy_server) const;
-
   // URL fetcher used for performing the secure proxy check.
   std::unique_ptr<SecureProxyChecker> secure_proxy_checker_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
index 0a2eaf0..f4329f7 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -159,10 +159,6 @@
 
     context_->Init();
 
-    // Disable fetching of warmup URL to avoid generating extra traffic which
-    // would need to be satisfied using mock sockets.
-    test_context_->DisableWarmupURLFetch();
-
     test_context_->InitSettings();
     ResetBackoffEntryReleaseTime();
     test_context_->test_config_client()->SetNow(base::Time::UnixEpoch());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
index 046df80..acd97fa 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
@@ -107,7 +107,6 @@
   void SetCurrentNetworkID(const std::string& network_id);
 
   using DataReductionProxyConfig::UpdateConfigForTesting;
-  using DataReductionProxyConfig::HandleWarmupFetcherResponse;
 
  private:
   bool GetIsCaptivePortal() const override;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
index 2e614778..faa0d94 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -75,8 +75,6 @@
   for (const net::ProxyServer& proxy : proxies_for_http)
     proxy_strings.push_back(proxy.ToURI());
 
-  // Proxies specified via kDataReductionProxyHttpProxies command line switch
-  // have type ProxyServer::UNSPECIFIED_TYPE.
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       data_reduction_proxy::switches::kDataReductionProxyHttpProxies,
       base::JoinString(proxy_strings, ";"));
@@ -1051,129 +1049,4 @@
       *request.get(), *previews_decider.get()));
 }
 
-TEST_F(DataReductionProxyConfigTest, HandleWarmupFetcherResponse) {
-  base::HistogramTester histogram_tester;
-  const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
-  const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
-      "https://origin.net:443", net::ProxyServer::SCHEME_HTTP);
-  const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
-      "fallback.net:80", net::ProxyServer::SCHEME_HTTP);
-  const net::ProxyServer kNonDataSaverProxy = net::ProxyServer::FromURI(
-      "https://non-data-saver-proxy.net:443", net::ProxyServer::SCHEME_HTTP);
-
-  SetProxiesForHttpOnCommandLine({kHttpsProxy, kHttpProxy});
-  ResetSettings();
-
-  // The proxy is enabled.
-  test_config()->UpdateConfigForTesting(true, true, true);
-  test_config()->OnNewClientConfigFetched();
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-
-  // Report failed warmup for a non-DataSaver proxy, and verify that it does not
-  // change the list of data saver proxies.
-  test_config()->HandleWarmupFetcherResponse(net::ProxyServer(),
-                                             false /* success_response */);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-
-  // Report successful warmup of |kHttpsProxy|.
-  test_config()->HandleWarmupFetcherResponse(kHttpsProxy, true);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-  histogram_tester.ExpectUniqueSample(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      1, 1);
-
-  // Report failed warmup |kHttpsProxy| and verify it is removed from the list
-  // of proxies.
-  test_config()->HandleWarmupFetcherResponse(kHttpsProxy, false);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      0, 1);
-
-  // Report failed warmup |kHttpsProxy| again, and verify it does not change the
-  // list of proxies.
-  test_config()->HandleWarmupFetcherResponse(kHttpsProxy, false);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      0, 2);
-
-  // |kHttpsProxy| should now be added back to the list of proxies.
-  test_config()->HandleWarmupFetcherResponse(kHttpsProxy, true);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      1, 2);
-
-  // Report successful warmup |kHttpsProxy| again, and verify that there is no
-  // change in the list of proxies..
-  test_config()->HandleWarmupFetcherResponse(kHttpsProxy, true);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      1, 3);
-
-  // |kHttpsProxy| should be removed again from the list of proxies.
-  test_config()->HandleWarmupFetcherResponse(kHttpsProxy, false);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      0, 3);
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      1, 3);
-
-  // Now report failed warmup for |kHttpProxy| and verify that it is also
-  // removed from the list of proxies.
-  test_config()->HandleWarmupFetcherResponse(kHttpProxy, false);
-  EXPECT_EQ(std::vector<net::ProxyServer>({}), GetConfiguredProxiesForHttp());
-  histogram_tester.ExpectUniqueSample(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "InsecureProxy.NonCore",
-      0, 1);
-
-  // Both proxies should be added back.
-  test_config()->HandleWarmupFetcherResponse(kHttpsProxy, true);
-  test_config()->HandleWarmupFetcherResponse(kHttpProxy, true);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      0, 3);
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "SecureProxy.NonCore",
-      1, 4);
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "InsecureProxy.NonCore",
-      0, 1);
-  histogram_tester.ExpectBucketCount(
-      "DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch."
-      "InsecureProxy.NonCore",
-      1, 1);
-
-  // If the warmup URL is unsuccessfully fetched using a non-data saver proxy,
-  // then there is no change in the list of proxies.
-  test_config()->HandleWarmupFetcherResponse(kNonDataSaverProxy, false);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-}
-
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc b/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
index 365de618..735738e 100644
--- a/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
+++ b/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
@@ -4,7 +4,6 @@
 
 #include "components/data_reduction_proxy/core/browser/warmup_url_fetcher.h"
 
-#include "base/callback.h"
 #include "base/guid.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
@@ -12,7 +11,6 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "net/base/load_flags.h"
-#include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -22,10 +20,8 @@
 
 WarmupURLFetcher::WarmupURLFetcher(
     const scoped_refptr<net::URLRequestContextGetter>&
-        url_request_context_getter,
-    WarmupURLFetcherCallback callback)
-    : url_request_context_getter_(url_request_context_getter),
-      callback_(callback) {
+        url_request_context_getter)
+    : url_request_context_getter_(url_request_context_getter) {
   DCHECK(url_request_context_getter_);
 }
 
@@ -115,23 +111,7 @@
                               util::ConvertNetProxySchemeToProxyScheme(
                                   source->ProxyServerUsed().scheme()),
                               PROXY_SCHEME_MAX);
-
-    if (!source->GetStatus().is_success() &&
-        source->GetStatus().error() == net::ERR_INTERNET_DISCONNECTED) {
-      // Fetching failed due to Internet unavailability, and not due to some
-      // error. Set the proxy server to unknown.
-      callback_.Run(net::ProxyServer(), true);
-      return;
-    }
   }
-
-  bool success_response =
-      source->GetStatus().status() == net::URLRequestStatus::SUCCESS &&
-      source->GetResponseCode() == net::HTTP_NO_CONTENT &&
-      source->GetResponseHeaders() &&
-      HasDataReductionProxyViaHeader(*(source->GetResponseHeaders()),
-                                     nullptr /* has_intermediary */);
-  callback_.Run(source->ProxyServerUsed(), success_response);
 }
 
 }  // namespace data_reduction_proxy
\ No newline at end of file
diff --git a/components/data_reduction_proxy/core/browser/warmup_url_fetcher.h b/components/data_reduction_proxy/core/browser/warmup_url_fetcher.h
index f470b32..0e9b61c 100644
--- a/components/data_reduction_proxy/core/browser/warmup_url_fetcher.h
+++ b/components/data_reduction_proxy/core/browser/warmup_url_fetcher.h
@@ -7,7 +7,6 @@
 
 #include <utility>
 
-#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "net/url_request/url_fetcher_delegate.h"
@@ -16,7 +15,6 @@
 
 namespace net {
 
-class ProxyServer;
 class URLFetcher;
 class URLRequestContextGetter;
 
@@ -27,14 +25,8 @@
 // URLFetcherDelegate for fetching the warmup URL.
 class WarmupURLFetcher : public net::URLFetcherDelegate {
  public:
-  // The proxy server that was used to fetch the request, and whether the fetch
-  // was successful.
-  typedef base::RepeatingCallback<void(const net::ProxyServer&, bool)>
-      WarmupURLFetcherCallback;
-
-  WarmupURLFetcher(const scoped_refptr<net::URLRequestContextGetter>&
-                       url_request_context_getter,
-                   WarmupURLFetcherCallback callback);
+  explicit WarmupURLFetcher(const scoped_refptr<net::URLRequestContextGetter>&
+                                url_request_context_getter);
 
   ~WarmupURLFetcher() override;
 
@@ -54,10 +46,6 @@
   // The URLFetcher being used for fetching the warmup URL.
   std::unique_ptr<net::URLFetcher> fetcher_;
 
-  // Callback that should be executed when the fetching of the warmup URL is
-  // completed.
-  WarmupURLFetcherCallback callback_;
-
   DISALLOW_COPY_AND_ASSIGN(WarmupURLFetcher);
 };
 
diff --git a/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc b/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
index 9821129..b8dad96 100644
--- a/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
@@ -28,33 +27,14 @@
  public:
   WarmupURLFetcherTest(const scoped_refptr<net::URLRequestContextGetter>&
                            url_request_context_getter)
-      : WarmupURLFetcher(url_request_context_getter,
-                         base::BindRepeating(
-                             &WarmupURLFetcherTest::HandleWarmupFetcherResponse,
-                             base::Unretained(this))) {}
+      : WarmupURLFetcher(url_request_context_getter) {}
 
   ~WarmupURLFetcherTest() override {}
 
-  size_t callback_received_count() const { return callback_received_count_; }
-  const net::ProxyServer& proxy_server_last() const {
-    return proxy_server_last_;
-  }
-  bool success_response_last() const { return success_response_last_; }
-
   using WarmupURLFetcher::FetchWarmupURL;
   using WarmupURLFetcher::GetWarmupURLWithQueryParam;
 
  private:
-  void HandleWarmupFetcherResponse(const net::ProxyServer& proxy_server,
-                                   bool success_response) {
-    callback_received_count_++;
-    proxy_server_last_ = proxy_server;
-    success_response_last_ = success_response;
-  }
-
-  size_t callback_received_count_ = 0;
-  net::ProxyServer proxy_server_last_;
-  bool success_response_last_ = false;
   DISALLOW_COPY_AND_ASSIGN(WarmupURLFetcherTest);
 };
 
@@ -90,7 +70,7 @@
   EXPECT_TRUE(query_param_different);
 }
 
-TEST(WarmupURLFetcherTest, TestSuccessfulFetchWarmupURLNoViaHeader) {
+TEST(WarmupURLFetcherTest, TestSuccessfulFetchWarmupURL) {
   base::HistogramTester histogram_tester;
   base::MessageLoopForIO message_loop;
   const std::string config = "foobarbaz";
@@ -133,66 +113,6 @@
       "DataReductionProxy.WarmupURL.ProxySchemeUsed",
       util::ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT),
       1);
-
-  EXPECT_EQ(1u, warmup_url_fetcher.callback_received_count());
-  EXPECT_EQ(net::ProxyServer::SCHEME_DIRECT,
-            warmup_url_fetcher.proxy_server_last().scheme());
-  // success_response_last() should be false since the response does not contain
-  // the via header.
-  EXPECT_FALSE(warmup_url_fetcher.success_response_last());
-}
-
-TEST(WarmupURLFetcherTest, TestSuccessfulFetchWarmupURLWithViaHeader) {
-  base::HistogramTester histogram_tester;
-  base::MessageLoopForIO message_loop;
-  const std::string config = "foobarbaz";
-  std::vector<std::unique_ptr<net::SocketDataProvider>> socket_data_providers;
-  net::MockClientSocketFactory mock_socket_factory;
-  net::MockRead success_reads[3];
-  success_reads[0] = net::MockRead(
-      "HTTP/1.1 204 OK\r\nVia: 1.1 Chrome-Compression-Proxy\r\n\r\n");
-  success_reads[1] = net::MockRead(net::ASYNC, config.c_str(), config.length());
-  success_reads[2] = net::MockRead(net::SYNCHRONOUS, net::OK);
-
-  socket_data_providers.push_back(
-      (base::MakeUnique<net::StaticSocketDataProvider>(
-          success_reads, arraysize(success_reads), nullptr, 0)));
-  mock_socket_factory.AddSocketDataProvider(socket_data_providers.back().get());
-
-  std::unique_ptr<net::TestURLRequestContext> test_request_context(
-      new net::TestURLRequestContext(true));
-
-  test_request_context->set_client_socket_factory(&mock_socket_factory);
-  test_request_context->Init();
-  scoped_refptr<net::URLRequestContextGetter> request_context_getter =
-      new net::TestURLRequestContextGetter(message_loop.task_runner(),
-                                           std::move(test_request_context));
-
-  WarmupURLFetcherTest warmup_url_fetcher(request_context_getter);
-  warmup_url_fetcher.FetchWarmupURL();
-  base::RunLoop().RunUntilIdle();
-
-  histogram_tester.ExpectUniqueSample(
-      "DataReductionProxy.WarmupURL.FetchInitiated", 1, 1);
-  histogram_tester.ExpectUniqueSample(
-      "DataReductionProxy.WarmupURL.FetchSuccessful", 1, 1);
-  histogram_tester.ExpectUniqueSample("DataReductionProxy.WarmupURL.NetError",
-                                      net::OK, 1);
-  histogram_tester.ExpectUniqueSample(
-      "DataReductionProxy.WarmupURL.HttpResponseCode", net::HTTP_NO_CONTENT, 1);
-  histogram_tester.ExpectUniqueSample(
-      "DataReductionProxy.WarmupURL.HasViaHeader", 1, 1);
-  histogram_tester.ExpectUniqueSample(
-      "DataReductionProxy.WarmupURL.ProxySchemeUsed",
-      util::ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT),
-      1);
-
-  EXPECT_EQ(1u, warmup_url_fetcher.callback_received_count());
-  EXPECT_EQ(net::ProxyServer::SCHEME_DIRECT,
-            warmup_url_fetcher.proxy_server_last().scheme());
-  // success_response_last() should be true since the response contains the via
-  // header.
-  EXPECT_TRUE(warmup_url_fetcher.success_response_last());
 }
 
 TEST(WarmupURLFetcherTest, TestConnectionResetFetchWarmupURL) {
@@ -235,10 +155,6 @@
                                     0);
   histogram_tester.ExpectTotalCount(
       "DataReductionProxy.WarmupURL.ProxySchemeUsed", 0);
-  EXPECT_EQ(1u, warmup_url_fetcher.callback_received_count());
-  EXPECT_EQ(net::ProxyServer::SCHEME_INVALID,
-            warmup_url_fetcher.proxy_server_last().scheme());
-  EXPECT_FALSE(warmup_url_fetcher.success_response_last());
 }
 
 }  // namespace
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_server.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_server.cc
index eccf2bd..ddb01aa 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_server.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_server.cc
@@ -50,10 +50,6 @@
   return net_proxy_servers;
 }
 
-bool DataReductionProxyServer::IsCoreProxy() const {
-  return proxy_type_ == ProxyServer_ProxyType_CORE;
-}
-
 ProxyServer_ProxyType DataReductionProxyServer::GetProxyTypeForTesting() const {
   return proxy_type_;
 }
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_server.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_server.h
index 24767124..000267c7 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_server.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_server.h
@@ -35,8 +35,6 @@
       const std::vector<DataReductionProxyServer>&
           data_reduction_proxy_servers);
 
-  bool IsCoreProxy() const;
-
   // Returns |proxy_type_| for verification by tests.
   ProxyServer_ProxyType GetProxyTypeForTesting() const;
 
diff --git a/components/omnibox/browser/base_search_provider.cc b/components/omnibox/browser/base_search_provider.cc
index e6fb1e6..4800d933 100644
--- a/components/omnibox/browser/base_search_provider.cc
+++ b/components/omnibox/browser/base_search_provider.cc
@@ -345,17 +345,9 @@
   if (IsNTPPage(page_classification))
     return false;
 
-  // Only allow HTTP URLs or HTTPS URLs.  For HTTPS URLs, require that either
-  // the appropriate feature flag is enabled or the URL is the same domain as
-  // the search provider.
-  const bool scheme_allowed =
-      (current_page_url.scheme() == url::kHttpScheme) ||
-      ((current_page_url.scheme() == url::kHttpsScheme) &&
-       (base::FeatureList::IsEnabled(
-            omnibox::kSearchProviderContextAllowHttpsUrls) ||
-        net::registry_controlled_domains::SameDomainOrHost(
-            current_page_url, suggest_url,
-            net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES)));
+  // Only allow HTTP URLs or HTTPS URLs.
+  const bool scheme_allowed = (current_page_url.scheme() == url::kHttpScheme) ||
+                              (current_page_url.scheme() == url::kHttpsScheme);
   if (!scheme_allowed)
     return false;
 
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 450d46f..1c03cbb 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -83,13 +83,6 @@
 #endif
 };
 
-// Feature used to enable the transmission of HTTPS URLs as part of the
-// context to the suggest server (assuming SearchProvider is permitted to
-// transmit URLs for context in the first place).
-const base::Feature kSearchProviderContextAllowHttpsUrls{
-    "OmniboixSearchProviderContextAllowHttpsUrls",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Feature used for the Zero Suggest Redirect to Chrome Field Trial.
 const base::Feature kZeroSuggestRedirectToChrome{
     "ZeroSuggestRedirectToChrome", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 0dffa8b..2ffeb6a 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -40,7 +40,6 @@
 extern const base::Feature kEnableClipboardProvider;
 extern const base::Feature kAndroidChromeHomePersonalizedSuggestions;
 extern const base::Feature kSearchProviderWarmUpOnFocus;
-extern const base::Feature kSearchProviderContextAllowHttpsUrls;
 extern const base::Feature kZeroSuggestRedirectToChrome;
 extern const base::Feature kZeroSuggestSwapTitleAndUrl;
 extern const base::Feature kDisplayTitleForCurrentUrl;
diff --git a/components/signin/ios/browser/BUILD.gn b/components/signin/ios/browser/BUILD.gn
index 58c4fb0..53bcf126 100644
--- a/components/signin/ios/browser/BUILD.gn
+++ b/components/signin/ios/browser/BUILD.gn
@@ -18,6 +18,8 @@
     "profile_oauth2_token_service_ios_delegate.mm",
     "profile_oauth2_token_service_ios_provider.h",
     "profile_oauth2_token_service_ios_provider.mm",
+    "wait_for_network_callback_helper.cc",
+    "wait_for_network_callback_helper.h",
   ]
 
   deps = [
@@ -71,6 +73,7 @@
     "account_consistency_service_unittest.mm",
     "active_state_manager_impl_unittest.mm",
     "profile_oauth2_token_service_ios_delegate_unittest.mm",
+    "wait_for_network_callback_helper_unittest.cc",
   ]
 
   deps = [
diff --git a/components/signin/ios/browser/ios_signin_client.cc b/components/signin/ios/browser/ios_signin_client.cc
index e98a753..dd6da1b 100644
--- a/components/signin/ios/browser/ios_signin_client.cc
+++ b/components/signin/ios/browser/ios_signin_client.cc
@@ -19,9 +19,9 @@
       signin_error_controller_(signin_error_controller),
       cookie_settings_(cookie_settings),
       host_content_settings_map_(host_content_settings_map),
-      token_web_data_(token_web_data) {
+      token_web_data_(token_web_data),
+      callback_helper_(std::make_unique<WaitForNetworkCallbackHelper>()) {
   signin_error_controller_->AddObserver(this);
-  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
 }
 
 IOSSigninClient::~IOSSigninClient() {
@@ -29,7 +29,7 @@
 }
 
 void IOSSigninClient::Shutdown() {
-  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+  callback_helper_.reset();
 }
 
 scoped_refptr<TokenWebData> IOSSigninClient::GetDatabase() {
@@ -89,24 +89,8 @@
   return subscription;
 }
 
-void IOSSigninClient::OnNetworkChanged(
-    net::NetworkChangeNotifier::ConnectionType type) {
-  if (type >= net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE)
-    return;
-
-  for (const base::Closure& callback : delayed_callbacks_)
-    callback.Run();
-
-  delayed_callbacks_.clear();
-}
-
 void IOSSigninClient::DelayNetworkCall(const base::Closure& callback) {
-  // Don't bother if we don't have any kind of network connection.
-  if (net::NetworkChangeNotifier::IsOffline()) {
-    delayed_callbacks_.push_back(callback);
-  } else {
-    callback.Run();
-  }
+  callback_helper_->HandleCallback(callback);
 }
 
 std::unique_ptr<GaiaAuthFetcher> IOSSigninClient::CreateGaiaAuthFetcher(
diff --git a/components/signin/ios/browser/ios_signin_client.h b/components/signin/ios/browser/ios_signin_client.h
index c65fdbb..010f5cd 100644
--- a/components/signin/ios/browser/ios_signin_client.h
+++ b/components/signin/ios/browser/ios_signin_client.h
@@ -12,13 +12,12 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "components/signin/core/browser/signin_error_controller.h"
-#include "net/base/network_change_notifier.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "components/signin/ios/browser/wait_for_network_callback_helper.h"
 
 // iOS specific signin client.
 class IOSSigninClient
     : public SigninClient,
-      public net::NetworkChangeNotifier::NetworkChangeObserver,
       public SigninErrorController::Observer {
  public:
   IOSSigninClient(
@@ -57,10 +56,6 @@
   // KeyedService implementation.
   void Shutdown() override;
 
-  // net::NetworkChangeController::NetworkChangeObserver implementation.
-  void OnNetworkChanged(
-      net::NetworkChangeNotifier::ConnectionType type) override;
-
  private:
   PrefService* pref_service_;
   net::URLRequestContextGetter* url_request_context_;
@@ -68,7 +63,7 @@
   scoped_refptr<content_settings::CookieSettings> cookie_settings_;
   scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
   scoped_refptr<TokenWebData> token_web_data_;
-  std::list<base::Closure> delayed_callbacks_;
+  std::unique_ptr<WaitForNetworkCallbackHelper> callback_helper_;
 
   DISALLOW_COPY_AND_ASSIGN(IOSSigninClient);
 };
diff --git a/components/signin/ios/browser/wait_for_network_callback_helper.cc b/components/signin/ios/browser/wait_for_network_callback_helper.cc
new file mode 100644
index 0000000..fcec9a5f
--- /dev/null
+++ b/components/signin/ios/browser/wait_for_network_callback_helper.cc
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/ios/browser/wait_for_network_callback_helper.h"
+
+WaitForNetworkCallbackHelper::WaitForNetworkCallbackHelper() {
+  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
+}
+
+WaitForNetworkCallbackHelper::~WaitForNetworkCallbackHelper() {
+  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+}
+
+void WaitForNetworkCallbackHelper::OnNetworkChanged(
+    net::NetworkChangeNotifier::ConnectionType type) {
+  if (net::NetworkChangeNotifier::IsOffline())
+    return;
+
+  for (const base::Closure& callback : delayed_callbacks_)
+    callback.Run();
+
+  delayed_callbacks_.clear();
+}
+
+void WaitForNetworkCallbackHelper::HandleCallback(
+    const base::Closure& callback) {
+  if (net::NetworkChangeNotifier::IsOffline()) {
+    delayed_callbacks_.push_back(callback);
+  } else {
+    callback.Run();
+  }
+}
diff --git a/components/signin/ios/browser/wait_for_network_callback_helper.h b/components/signin/ios/browser/wait_for_network_callback_helper.h
new file mode 100644
index 0000000..6e77513
--- /dev/null
+++ b/components/signin/ios/browser/wait_for_network_callback_helper.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SIGNIN_IOS_BROWSER_WAIT_FOR_NETWORK_CALLBACK_HELPER_H_
+#define COMPONENTS_SIGNIN_IOS_BROWSER_WAIT_FOR_NETWORK_CALLBACK_HELPER_H_
+
+#include <list>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "net/base/network_change_notifier.h"
+
+// Class used for delaying callbacks when the network connection is offline and
+// invoking them when the network connection becomes online.
+class WaitForNetworkCallbackHelper
+    : public net::NetworkChangeNotifier::NetworkChangeObserver {
+ public:
+  WaitForNetworkCallbackHelper();
+  ~WaitForNetworkCallbackHelper() override;
+
+  // net::NetworkChangeController::NetworkChangeObserver implementation.
+  void OnNetworkChanged(
+      net::NetworkChangeNotifier::ConnectionType type) override;
+
+  // If offline, saves the |callback| to be called later when online. Otherwise,
+  // invokes immediately.
+  void HandleCallback(const base::Closure& callback);
+
+ private:
+  std::list<base::Closure> delayed_callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaitForNetworkCallbackHelper);
+};
+
+#endif  // COMPONENTS_SIGNIN_IOS_BROWSER_WAIT_FOR_NETWORK_CALLBACK_HELPER_H_
diff --git a/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc b/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc
new file mode 100644
index 0000000..78fe1ca
--- /dev/null
+++ b/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/ios/browser/wait_for_network_callback_helper.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "net/base/mock_network_change_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// A test fixture to test WaitForNetworkCallbackHelper.
+class WaitForNetworkCallbackHelperTest : public testing::Test {
+ public:
+  void CallbackFunction() { num_callbacks_invoked_++; }
+
+ protected:
+  WaitForNetworkCallbackHelperTest() : num_callbacks_invoked_(0) {}
+
+  int num_callbacks_invoked_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  net::test::MockNetworkChangeNotifier network_change_notifier_;
+  WaitForNetworkCallbackHelper callback_helper_;
+};
+
+TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedImmediately) {
+  network_change_notifier_.SetConnectionType(
+      net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
+  callback_helper_.HandleCallback(
+      base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction,
+                 base::Unretained(this)));
+  EXPECT_EQ(1, num_callbacks_invoked_);
+}
+
+TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedLater) {
+  network_change_notifier_.SetConnectionType(
+      net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE);
+  callback_helper_.HandleCallback(
+      base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction,
+                 base::Unretained(this)));
+  callback_helper_.HandleCallback(
+      base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction,
+                 base::Unretained(this)));
+  EXPECT_EQ(0, num_callbacks_invoked_);
+
+  network_change_notifier_.SetConnectionType(
+      net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
+  network_change_notifier_.NotifyObserversOfConnectionTypeChangeForTests(
+      net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ(2, num_callbacks_invoked_);
+}
diff --git a/components/sync/engine_impl/cycle/nudge_tracker.cc b/components/sync/engine_impl/cycle/nudge_tracker.cc
index 22f20e2d..40d26e5 100644
--- a/components/sync/engine_impl/cycle/nudge_tracker.cc
+++ b/components/sync/engine_impl/cycle/nudge_tracker.cc
@@ -142,7 +142,7 @@
 base::TimeDelta NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) {
   for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
     TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(it.Get());
-    DCHECK(tracker_it != type_trackers_.end());
+    DCHECK(tracker_it != type_trackers_.end()) << ModelTypeToString(it.Get());
     tracker_it->second->RecordLocalRefreshRequest();
   }
   return local_refresh_nudge_delay_;
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 3da036b..0873afd 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -829,6 +829,19 @@
     data->display->Resize(size);
 }
 
+void GpuProcessTransportFactory::SetDisplayColorMatrix(
+    ui::Compositor* compositor,
+    const SkMatrix44& matrix) {
+  PerCompositorDataMap::iterator it = per_compositor_data_.find(compositor);
+  if (it == per_compositor_data_.end())
+    return;
+  PerCompositorData* data = it->second.get();
+  DCHECK(data);
+
+  if (data->display)
+    data->display->SetColorMatrix(matrix);
+}
+
 void GpuProcessTransportFactory::SetDisplayColorSpace(
     ui::Compositor* compositor,
     const gfx::ColorSpace& blending_color_space,
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h
index a2c6a967..f59fcc5 100644
--- a/content/browser/compositor/gpu_process_transport_factory.h
+++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -86,6 +86,8 @@
   void SetDisplayVisible(ui::Compositor* compositor, bool visible) override;
   void ResizeDisplay(ui::Compositor* compositor,
                      const gfx::Size& size) override;
+  void SetDisplayColorMatrix(ui::Compositor* compositor,
+                             const SkMatrix44& matrix) override;
   void SetDisplayColorSpace(ui::Compositor* compositor,
                             const gfx::ColorSpace& blending_color_space,
                             const gfx::ColorSpace& output_color_space) override;
diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc
index 2ce0aa2..888831be 100644
--- a/content/browser/compositor/viz_process_transport_factory.cc
+++ b/content/browser/compositor/viz_process_transport_factory.cc
@@ -248,6 +248,15 @@
   // Do nothing and resize when a CompositorFrame with a new size arrives.
 }
 
+void VizProcessTransportFactory::SetDisplayColorMatrix(
+    ui::Compositor* compositor,
+    const SkMatrix44& matrix) {
+  auto iter = compositor_data_map_.find(compositor);
+  if (iter == compositor_data_map_.end() || !iter->second.display_private)
+    return;
+  iter->second.display_private->SetDisplayColorMatrix(gfx::Transform(matrix));
+}
+
 void VizProcessTransportFactory::SetDisplayColorSpace(
     ui::Compositor* compositor,
     const gfx::ColorSpace& blending_color_space,
diff --git a/content/browser/compositor/viz_process_transport_factory.h b/content/browser/compositor/viz_process_transport_factory.h
index 61e4cea..76f3201 100644
--- a/content/browser/compositor/viz_process_transport_factory.h
+++ b/content/browser/compositor/viz_process_transport_factory.h
@@ -82,6 +82,8 @@
   void SetDisplayVisible(ui::Compositor* compositor, bool visible) override;
   void ResizeDisplay(ui::Compositor* compositor,
                      const gfx::Size& size) override;
+  void SetDisplayColorMatrix(ui::Compositor* compositor,
+                             const SkMatrix44& matrix) override;
   void SetDisplayColorSpace(ui::Compositor* compositor,
                             const gfx::ColorSpace& blending_color_space,
                             const gfx::ColorSpace& output_color_space) override;
diff --git a/content/browser/download/parallel_download_utils.cc b/content/browser/download/parallel_download_utils.cc
index 49fc009..f559998 100644
--- a/content/browser/download/parallel_download_utils.cc
+++ b/content/browser/download/parallel_download_utils.cc
@@ -15,15 +15,15 @@
 namespace {
 
 // Default value for |kMinSliceSizeFinchKey|, when no parameter is specified.
-const int64_t kMinSliceSizeParallelDownload = 2097152;
+const int64_t kMinSliceSizeParallelDownload = 1365333;
 
 // Default value for |kParallelRequestCountFinchKey|, when no parameter is
 // specified.
-const int kParallelRequestCount = 2;
+const int kParallelRequestCount = 3;
 
 // The default remaining download time in seconds required for parallel request
 // creation.
-const int kDefaultRemainingTimeInSeconds = 10;
+const int kDefaultRemainingTimeInSeconds = 2;
 
 // TODO(qinmin): replace this with a comparator operator in
 // DownloadItem::ReceivedSlice.
diff --git a/content/browser/media/media_canplaytype_browsertest.cc b/content/browser/media/media_canplaytype_browsertest.cc
index 15bd97d..db8a7fe 100644
--- a/content/browser/media/media_canplaytype_browsertest.cc
+++ b/content/browser/media/media_canplaytype_browsertest.cc
@@ -324,10 +324,6 @@
     EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"1\"'"));
     EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"theora, 1\"'"));
 
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"vp8\"'"));
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"vp8.0\"'"));
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"vp8, opus\"'"));
-    EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"vp8, vorbis\"'"));
     EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"vp08\"'"));
     EXPECT_EQ(kNot,
               CanPlay("'" + mime + "; codecs=\"vp08.00.01.08.02.01.01.00\"'"));
@@ -599,7 +595,7 @@
   // by default. This test needs to be merged into the existing mp4 and webm
   // before release as well. http://crbug.com/784607
   EXPECT_EQ(kProbably, CanPlay("'video/webm; codecs=\"av1\"'"));
-  EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"av1\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'video/mp4; codecs=\"av1\"'"));
 }
 #endif  // BUILDFLAG(ENABLE_AV1_DECODER)
 
@@ -676,6 +672,10 @@
             CanPlay("'video/ogg; codecs=\"theora, vorbis\"'"));
   EXPECT_EQ(kOggVideoProbably,
             CanPlay("'video/ogg; codecs=\"flac, opus, vorbis\"'"));
+  EXPECT_EQ(kOggVideoProbably, CanPlay("'video/ogg; codecs=\"vp8\"'"));
+  EXPECT_EQ(kOggVideoProbably, CanPlay("'video/ogg; codecs=\"vp8.0\"'"));
+  EXPECT_EQ(kOggVideoProbably, CanPlay("'video/ogg; codecs=\"vp8, opus\"'"));
+  EXPECT_EQ(kOggVideoProbably, CanPlay("'video/ogg; codecs=\"vp8, vorbis\"'"));
 
   TestOGGUnacceptableCombinations("video/ogg");
 
@@ -714,8 +714,8 @@
   EXPECT_EQ(kProbably, CanPlay("'audio/ogg; codecs=\"flac\"'"));
 
   // See CodecSupportTest_mp4 for more flac combos.
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"flac\"'"));
-  EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"flac\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"flac\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'video/mp4; codecs=\"flac\"'"));
 
   EXPECT_EQ(kNot, CanPlay("'video/flac'"));
   EXPECT_EQ(kNot, CanPlay("'video/x-flac'"));
@@ -744,11 +744,11 @@
   EXPECT_EQ(kNot, CanPlay("'video/x-mp3'"));
 
   // audio/mpeg without a codecs parameter (RFC 3003 compliant)
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mpeg'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mpeg'"));
 
   // audio/mpeg with mp3 in codecs parameter. (Not RFC compliant, but
   // very common in the wild so it is a defacto standard).
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mpeg; codecs=\"mp3\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mpeg; codecs=\"mp3\"'"));
 
   EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc1\"'"));
   EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc3\"'"));
@@ -760,8 +760,8 @@
   EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.67\"'"));
   EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.68\"'"));
   // The next two results are wrong due to https://crbug.com/592889.
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mpeg; codecs=\"mp4a.69\"'"));
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mpeg; codecs=\"mp4a.6B\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mpeg; codecs=\"mp4a.69\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mpeg; codecs=\"mp4a.6B\"'"));
   EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.40.2\"'"));
   EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.40.02\"'"));
 
@@ -770,7 +770,7 @@
   TestMPEGUnacceptableCombinations("audio/mpeg");
 
   // audio/mp3 does not allow any codecs parameter
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mp3'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp3'"));
 
   EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc1\"'"));
   EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc3\"'"));
@@ -792,7 +792,7 @@
   EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"mp3\"'"));
 
   // audio/x-mp3 does not allow any codecs parameter
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/x-mp3'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/x-mp3'"));
 
   EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc1\"'"));
   EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc3\"'"));
@@ -815,7 +815,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) {
-  EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4'"));
+  EXPECT_EQ(kMaybe, CanPlay("'video/mp4'"));
 
   EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1\"'"));
   EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc3\"'"));
@@ -838,8 +838,8 @@
   EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.66\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.67\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.68\"'"));
-  EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.69\"'"));
-  EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.6B\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'video/mp4; codecs=\"mp4a.69\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'video/mp4; codecs=\"mp4a.6B\"'"));
 
   EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.40.2\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.40.02\"'"));
@@ -890,8 +890,8 @@
   EXPECT_EQ(kHevcSupported,
             CanPlay("'video/mp4; codecs=\"hvc1.1.6.L93.B0, mp4a.40.5\"'"));
 
-  EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"vp09.00.10.08\"'"));
-  EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"flac\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'video/mp4; codecs=\"vp09.00.10.08\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'video/mp4; codecs=\"flac\"'"));
   EXPECT_EQ(kPropProbably,
             CanPlay("'video/mp4; codecs=\"avc1.4D401E, flac\"'"));
   EXPECT_EQ(kPropProbably,
@@ -899,7 +899,7 @@
 
   TestMPEGUnacceptableCombinations("video/mp4");
   // This result is incorrect. See https://crbug.com/592889.
-  EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp3\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'video/mp4; codecs=\"mp3\"'"));
 
   EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v'"));
 
@@ -973,12 +973,12 @@
 
   TestMPEGUnacceptableCombinations("video/x-m4v");
 
-  EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4'"));
+  EXPECT_EQ(kMaybe, CanPlay("'audio/mp4'"));
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.66\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.67\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.68\"'"));
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.69\"'"));
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.6B\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"mp4a.69\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"mp4a.6B\"'"));
 
   EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"mp4a.40\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.40.2\"'"));
@@ -987,7 +987,7 @@
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.40.05\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.40.29\"'"));
 
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"flac\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"flac\"'"));
 
   EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc1\"'"));
   EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc3\"'"));
@@ -1013,7 +1013,7 @@
 
   TestMPEGUnacceptableCombinations("audio/mp4");
   // This result is incorrect. See https://crbug.com/592889.
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp3\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"mp3\"'"));
 
   EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a'"));
 
@@ -1366,10 +1366,10 @@
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.67\"'"));
   EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.68\"'"));
   // MP3.
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.69\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"mp4a.69\"'"));
   EXPECT_EQ(kNot,          CanPlay("'audio/mp4; codecs=\"mp4a.6A\"'"));
   // MP3.
-  EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.6B\"'"));
+  EXPECT_EQ(kProbably, CanPlay("'audio/mp4; codecs=\"mp4a.6B\"'"));
   EXPECT_EQ(kNot,          CanPlay("'audio/mp4; codecs=\"mp4a.6b\"'"));
   EXPECT_EQ(kNot,          CanPlay("'audio/mp4; codecs=\"mp4a.6C\"'"));
   EXPECT_EQ(kNot,          CanPlay("'audio/mp4; codecs=\"mp4a.6D\"'"));
@@ -1540,16 +1540,11 @@
 IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_NewVp9Variants) {
   const std::string kSupportedMimeTypes[] = {"video/webm", "video/mp4"};
   for (const auto& mime_type : kSupportedMimeTypes) {
-    // MP4 support is conditional on supporting proprietary codecs.
-    const char* kTestProbably = kProbably;
-    if (base::EndsWith(mime_type, "mp4", base::CompareCase::SENSITIVE))
-      kTestProbably = kPropProbably;
-
 // Profile 2 and 3 support is currently disabled on ARM and MIPS.
 #if defined(ARCH_CPU_ARM_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
     const char* kVP9Profile2And3Probably = kNot;
 #else
-    const char* kVP9Profile2And3Probably = kTestProbably;
+    const char* kVP9Profile2And3Probably = kProbably;
 #endif
 
     // E.g. "'video/webm; "
@@ -1559,15 +1554,15 @@
     EXPECT_EQ(kNot, CanPlay(prefix + "codecs=\"vp09.00.-1.08\"'"));
 
     // Test a few valid strings.
-    EXPECT_EQ(kTestProbably, CanPlay(prefix + "codecs=\"vp09.00.10.08\"'"));
-    EXPECT_EQ(kTestProbably,
+    EXPECT_EQ(kProbably, CanPlay(prefix + "codecs=\"vp09.00.10.08\"'"));
+    EXPECT_EQ(kProbably,
               CanPlay(prefix + "codecs=\"vp09.00.10.08.00.01.01.01.00\"'"));
-    EXPECT_EQ(kTestProbably,
+    EXPECT_EQ(kProbably,
               CanPlay(prefix + "codecs=\"vp09.00.10.08.01.02.02.02.00\"'"));
 
     // Profiles 0 and 1 are always supported supported. Profiles 2 and 3 are
     // only supported on certain architectures.
-    EXPECT_EQ(kTestProbably, CanPlay(prefix + "codecs=\"vp09.01.10.08\"'"));
+    EXPECT_EQ(kProbably, CanPlay(prefix + "codecs=\"vp09.01.10.08\"'"));
     EXPECT_EQ(kVP9Profile2And3Probably,
               CanPlay(prefix + "codecs=\"vp09.02.10.08\"'"));
     EXPECT_EQ(kVP9Profile2And3Probably,
diff --git a/content/browser/media/media_capabilities_browsertest.cc b/content/browser/media/media_capabilities_browsertest.cc
index e8084e8..3c3e816 100644
--- a/content/browser/media/media_capabilities_browsertest.cc
+++ b/content/browser/media/media_capabilities_browsertest.cc
@@ -133,6 +133,11 @@
       kSupported,
       CanDecodeVideo(config_type, "'video/webm; codecs=\"vp09.00.10.08\"'"));
 
+  // VP09 is available in MP4 container irrespective of USE_PROPRIETARY_CODECS.
+  EXPECT_EQ(
+      kSupported,
+      CanDecodeVideo(config_type, "'video/mp4; codecs=\"vp09.00.10.08\"'"));
+
   // Supported when built with USE_PROPRIETARY_CODECS
   EXPECT_EQ(kPropSupported,
             CanDecodeVideo(config_type, "'video/mp4; codecs=\"avc1.42E01E\"'"));
@@ -142,9 +147,6 @@
             CanDecodeVideo(config_type, "'video/mp4; codecs=\"avc1.42701E\"'"));
   EXPECT_EQ(kPropSupported,
             CanDecodeVideo(config_type, "'video/mp4; codecs=\"avc1.42F01E\"'"));
-  EXPECT_EQ(
-      kPropSupported,
-      CanDecodeVideo(config_type, "'video/mp4; codecs=\"vp09.00.10.08\"'"));
 
   // Test a handful of invalid strings.
   EXPECT_EQ(kUnsupported,
@@ -172,14 +174,14 @@
             CanDecodeAudio(config_type, "'audio/webm; codecs=\"opus\"'"));
   EXPECT_EQ(kSupported,
             CanDecodeAudio(config_type, "'audio/webm; codecs=\"vorbis\"'"));
+  EXPECT_EQ(kSupported,
+            CanDecodeAudio(config_type, "'audio/mp4; codecs=\"flac\"'"));
+  EXPECT_EQ(kSupported, CanDecodeAudio(config_type, "'audio/mpeg'"));
 
   // Supported when built with USE_PROPRIETARY_CODECS
   EXPECT_EQ(kPropSupported,
             CanDecodeAudio(config_type, "'audio/mp4; codecs=\"mp4a.40.02\"'"));
   EXPECT_EQ(kPropSupported, CanDecodeAudio(config_type, "'audio/aac'"));
-  EXPECT_EQ(kPropSupported,
-            CanDecodeAudio(config_type, "'audio/mp4; codecs=\"flac\"'"));
-  EXPECT_EQ(kPropSupported, CanDecodeAudio(config_type, "'audio/mpeg'"));
 
   // Test a handful of invalid strings.
   EXPECT_EQ(kUnsupported,
@@ -219,7 +221,7 @@
             CanDecodeAudio(config_type, "'audio/ogg; codecs=\"opus\"'"));
 
   // MP3 is only supported via audio/mpeg for MSE.
-  EXPECT_EQ(prop_type_supported,
+  EXPECT_EQ(type_supported,
             CanDecodeAudio(config_type, "'audio/mp4; codecs=\"mp4a.69\"'"));
 
   // Ogg not supported in MSE.
diff --git a/content/browser/pointer_lock_browsertest.cc b/content/browser/pointer_lock_browsertest.cc
index dc8530c0..42070cd 100644
--- a/content/browser/pointer_lock_browsertest.cc
+++ b/content/browser/pointer_lock_browsertest.cc
@@ -33,7 +33,6 @@
   MockRenderWidgetHostView(RenderWidgetHost* host, bool is_guest_view_hack)
       : RenderWidgetHostViewAura(host,
                                  is_guest_view_hack,
-                                 false /* enable_surface_synchronization */,
                                  false /* is_mus_browser_plugin_guest */),
         host_(RenderWidgetHostImpl::From(host)) {}
   ~MockRenderWidgetHostView() override {
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index d9d14af8..175c06d 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -86,7 +86,8 @@
 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) {
   frame_evictor_->SetVisible(true);
 
-  if (!has_primary_surface_ && !released_front_lock_.get()) {
+  if (!enable_surface_synchronization_ && !HasFallbackSurface() &&
+      !released_front_lock_.get()) {
     if (compositor_)
       released_front_lock_ = compositor_->GetCompositorLock(nullptr);
   }
@@ -116,7 +117,7 @@
           switches::kDisableResizeLock))
     return;
 
-  if (!has_primary_surface_)
+  if (!HasFallbackSurface())
     return;
 
   if (!client_->DelegatedFrameCanCreateResizeLock())
@@ -237,7 +238,7 @@
     const gfx::PointF& point,
     RenderWidgetHostViewBase* target_view,
     gfx::PointF* transformed_point) {
-  if (!has_primary_surface_)
+  if (!HasFallbackSurface())
     return false;
 
   return target_view->TransformPointToLocalCoordSpace(
@@ -265,6 +266,18 @@
   support_->DidNotProduceFrame(ack);
 }
 
+bool DelegatedFrameHost::HasPrimarySurface() const {
+  const viz::SurfaceId* primary_surface_id =
+      client_->DelegatedFrameHostGetLayer()->GetPrimarySurfaceId();
+  return primary_surface_id && primary_surface_id->is_valid();
+}
+
+bool DelegatedFrameHost::HasFallbackSurface() const {
+  const viz::SurfaceId* fallback_surface_id =
+      client_->DelegatedFrameHostGetLayer()->GetFallbackSurfaceId();
+  return fallback_surface_id && fallback_surface_id->is_valid();
+}
+
 bool DelegatedFrameHost::ShouldSkipFrame(const gfx::Size& size_in_dip) {
   if (!resize_lock_)
     return false;
@@ -286,20 +299,13 @@
 }
 
 void DelegatedFrameHost::WasResized() {
-  if (client_->DelegatedFrameHostDesiredSizeInDIP() !=
-          current_frame_size_in_dip_ &&
-      !client_->DelegatedFrameHostIsVisible()) {
-    EvictDelegatedFrame();
-  }
-
-  if (enable_surface_synchronization_) {
+  if (enable_surface_synchronization_ &&
+      client_->DelegatedFrameHostIsVisible()) {
     current_frame_size_in_dip_ = client_->DelegatedFrameHostDesiredSizeInDIP();
 
     viz::SurfaceId surface_id(frame_sink_id_, client_->GetLocalSurfaceId());
     client_->DelegatedFrameHostGetLayer()->SetShowPrimarySurface(
         surface_id, current_frame_size_in_dip_, GetSurfaceReferenceFactory());
-    has_primary_surface_ = true;
-    frame_evictor_->SwappedFrame(client_->DelegatedFrameHostIsVisible());
     if (compositor_)
       compositor_->OnChildResizing();
     // Input throttling and guttering are handled differently when surface
@@ -307,6 +313,12 @@
     return;
   }
 
+  if (client_->DelegatedFrameHostDesiredSizeInDIP() !=
+          current_frame_size_in_dip_ &&
+      !client_->DelegatedFrameHostIsVisible()) {
+    EvictDelegatedFrame();
+  }
+
   // If |create_resize_lock_after_commit_| is true, we're waiting to recreate
   // an expired resize lock after the next UI frame is submitted, so don't
   // make a lock here.
@@ -323,7 +335,7 @@
 }
 
 void DelegatedFrameHost::UpdateGutters() {
-  if (!has_primary_surface_ || enable_surface_synchronization_) {
+  if (!HasFallbackSurface() || enable_surface_synchronization_) {
     right_gutter_.reset();
     bottom_gutter_.reset();
     return;
@@ -429,7 +441,7 @@
 
   // To avoid unnecessary browser composites, try to go directly to the Surface
   // rather than through the Layer (which goes through the browser compositor).
-  if (has_primary_surface_ &&
+  if (HasFallbackSurface() &&
       request_copy_of_output_callback_for_testing_.is_null()) {
     support_->RequestCopyOfSurface(std::move(request));
   } else {
@@ -508,15 +520,8 @@
     bool result = support_->SubmitCompositorFrame(
         local_surface_id, std::move(frame), std::move(hit_test_region_list));
     DCHECK(result);
-
-    DCHECK(enable_surface_synchronization_ || has_primary_surface_);
   }
 
-  if (!enable_surface_synchronization_) {
-    if (has_primary_surface_)
-      frame_evictor_->SwappedFrame(client_->DelegatedFrameHostIsVisible());
-    // Note: the frame may have been evicted immediately.
-  }
 }
 
 void DelegatedFrameHost::ClearDelegatedFrame() {
@@ -565,27 +570,24 @@
   if (!enable_surface_synchronization_) {
     client_->DelegatedFrameHostGetLayer()->SetShowPrimarySurface(
         surface_info.id(), frame_size_in_dip, GetSurfaceReferenceFactory());
-    has_primary_surface_ = true;
   }
 
-  // If surface synchronization is enabled, and we don't have a primary surface
-  // then that means it has been evicted, and so we have nothing to do here.
-  if (!has_primary_surface_)
-    return;
-
   client_->DelegatedFrameHostGetLayer()->SetFallbackSurfaceId(
       surface_info.id());
   local_surface_id_ = surface_info.id().local_surface_id();
 
   // Surface synchronization deals with resizes in WasResized().
-  if (enable_surface_synchronization_)
-    return;
+  if (!enable_surface_synchronization_) {
+    released_front_lock_ = nullptr;
+    current_frame_size_in_dip_ = frame_size_in_dip;
+    CheckResizeLock();
 
-  released_front_lock_ = nullptr;
-  current_frame_size_in_dip_ = frame_size_in_dip;
-  CheckResizeLock();
+    UpdateGutters();
+  }
 
-  UpdateGutters();
+  if (HasFallbackSurface())
+    frame_evictor_->SwappedFrame(client_->DelegatedFrameHostIsVisible());
+  // Note: the frame may have been evicted immediately.
 }
 
 void DelegatedFrameHost::OnFrameTokenChanged(uint32_t frame_token) {
@@ -604,11 +606,19 @@
     return;
   }
 
-  if (!has_primary_surface_)
+  if (!HasFallbackSurface())
     return;
+
+  if (enable_surface_synchronization_) {
+    client_->DelegatedFrameHostGetLayer()->SetFallbackSurfaceId(
+        viz::SurfaceId());
+    support_->EvictCurrentSurface();
+    frame_evictor_->DiscardedFrame();
+    return;
+  }
+
   client_->DelegatedFrameHostGetLayer()->SetShowSolidColorContent();
   support_->EvictCurrentSurface();
-  has_primary_surface_ = false;
   resize_lock_.reset();
   frame_evictor_->DiscardedFrame();
   UpdateGutters();
diff --git a/content/browser/renderer_host/delegated_frame_host.h b/content/browser/renderer_host/delegated_frame_host.h
index 6766d00..c7a0c51 100644
--- a/content/browser/renderer_host/delegated_frame_host.h
+++ b/content/browser/renderer_host/delegated_frame_host.h
@@ -198,7 +198,8 @@
     return support_.get();
   }
 
-  bool HasPrimarySurfaceForTesting() const { return has_primary_surface_; }
+  bool HasPrimarySurface() const;
+  bool HasFallbackSurface() const;
 
   void OnCompositingDidCommitForTesting(ui::Compositor* compositor) {
     OnCompositingDidCommit(compositor);
@@ -346,7 +347,6 @@
 
   bool needs_begin_frame_ = false;
 
-  bool has_primary_surface_ = false;
   viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink_ =
       nullptr;
 
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 429dff66..0f867aa 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -1099,8 +1099,7 @@
   std::unique_ptr<RenderWidgetHostViewBase> view;
 #if defined(USE_AURA)
   view.reset(new RenderWidgetHostViewAura(
-      host_.get(), false, false /* enable_surface_synchronization */,
-      false /* is_mus_browser_plugin_guest */));
+      host_.get(), false, false /* is_mus_browser_plugin_guest */));
   // TODO(derat): Call this on all platforms: http://crbug.com/102450.
   view->InitAsChild(nullptr);
 #elif defined(OS_ANDROID)
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 46a5b64..10c475f 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -20,6 +21,7 @@
 #include "build/build_config.h"
 #include "cc/layers/layer.h"
 #include "cc/trees/layer_tree_settings.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
 #include "components/viz/common/gl_helper.h"
@@ -389,10 +391,8 @@
 RenderWidgetHostViewAura::RenderWidgetHostViewAura(
     RenderWidgetHost* host,
     bool is_guest_view_hack,
-    bool enable_surface_synchronization,
     bool is_mus_browser_plugin_guest)
     : host_(RenderWidgetHostImpl::From(host)),
-      enable_surface_synchronization_(enable_surface_synchronization),
       is_mus_browser_plugin_guest_(is_mus_browser_plugin_guest),
       window_(nullptr),
       in_shutdown_(false),
@@ -1988,7 +1988,7 @@
       base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableViz);
   delegated_frame_host_ = std::make_unique<DelegatedFrameHost>(
       frame_sink_id_, delegated_frame_host_client_.get(),
-      enable_surface_synchronization_, enable_viz);
+      features::IsSurfaceSynchronizationEnabled(), enable_viz);
 
   if (renderer_compositor_frame_sink_) {
     delegated_frame_host_->DidCreateNewRendererCompositorFrameSink(
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index 8e29d81..80e0dee 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -97,7 +97,6 @@
   // |is_mus_browser_plugin_guest| can be removed at the same time.
   RenderWidgetHostViewAura(RenderWidgetHost* host,
                            bool is_guest_view_hack,
-                           bool enable_surface_synchronization,
                            bool is_mus_browser_plugin_guest);
 
   // RenderWidgetHostView implementation.
@@ -512,8 +511,6 @@
   // The model object.
   RenderWidgetHostImpl* const host_;
 
-  const bool enable_surface_synchronization_;
-
   const bool is_mus_browser_plugin_guest_;
 
   // NOTE: this is null if |is_mus_browser_plugin_guest_| is true.
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index a75f6aa0..10cab06 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/gl_helper.h"
@@ -499,11 +500,9 @@
 class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {
  public:
   FakeRenderWidgetHostViewAura(RenderWidgetHost* widget,
-                               bool is_guest_view_hack,
-                               bool enable_surface_synchronization)
+                               bool is_guest_view_hack)
       : RenderWidgetHostViewAura(widget,
                                  is_guest_view_hack,
-                                 enable_surface_synchronization,
                                  false /* is_mus_browser_plugin_guest */),
         delegated_frame_host_client_(
             new FakeDelegatedFrameHostClientAura(this)) {
@@ -560,7 +559,11 @@
   }
 
   bool HasPrimarySurface() const {
-    return GetDelegatedFrameHost()->HasPrimarySurfaceForTesting();
+    return GetDelegatedFrameHost()->HasPrimarySurface();
+  }
+
+  bool HasFallbackSurface() const {
+    return GetDelegatedFrameHost()->HasFallbackSurface();
   }
 
   bool released_front_lock_active() const {
@@ -707,7 +710,7 @@
         std::move(delegated_frame_host_client);
   }
 
-  void SetUpEnvironment(bool enable_surface_synchronization) {
+  void SetUpEnvironment() {
     mojo_feature_list_.InitAndEnableFeature(features::kMojoInputMessages);
     ImageTransportFactory::SetFactory(
         std::make_unique<NoTransportImageTransportFactory>());
@@ -730,8 +733,7 @@
     delegates_.back()->set_widget_host(parent_host_);
     const bool is_mus_browser_plugin_guest = false;
     parent_view_ = new RenderWidgetHostViewAura(
-        parent_host_, is_guest_view_hack_, enable_surface_synchronization,
-        is_mus_browser_plugin_guest);
+        parent_host_, is_guest_view_hack_, is_mus_browser_plugin_guest);
     parent_view_->InitAsChild(nullptr);
     aura::client::ParentWindowWithContext(parent_view_->GetNativeView(),
                                           aura_test_helper_->root_window(),
@@ -743,8 +745,7 @@
                                                     process_host_, routing_id);
     delegates_.back()->set_widget_host(widget_host_);
     widget_host_->Init();
-    view_ = new FakeRenderWidgetHostViewAura(widget_host_, is_guest_view_hack_,
-                                             enable_surface_synchronization);
+    view_ = new FakeRenderWidgetHostViewAura(widget_host_, is_guest_view_hack_);
     base::RunLoop().RunUntilIdle();
   }
 
@@ -777,9 +778,7 @@
     ImageTransportFactory::Terminate();
   }
 
-  void SetUp() override {
-    SetUpEnvironment(false /* enable_surface_synchronization */);
-  }
+  void SetUp() override { SetUpEnvironment(); }
 
   void TearDown() override { TearDownEnvironment(); }
 
@@ -912,8 +911,13 @@
 class RenderWidgetHostViewAuraSurfaceSynchronizationTest
     : public RenderWidgetHostViewAuraTest {
   void SetUp() override {
-    SetUpEnvironment(true /* enable_surface_synchronization */);
+    surface_synchronization_feature_list_.InitAndEnableFeature(
+        features::kEnableSurfaceSynchronization);
+    SetUpEnvironment();
   }
+
+ private:
+  base::test::ScopedFeatureList surface_synchronization_feature_list_;
 };
 
 class RenderWidgetHostViewAuraWheelScrollLatchingEnabledTest
@@ -3123,7 +3127,7 @@
   view_->SetSize(gfx::Size(300, 300));
   ASSERT_TRUE(view_->HasPrimarySurface());
   EXPECT_EQ(gfx::Size(300, 300), view_->window_->layer()->size());
-  EXPECT_FALSE(view_->window_->layer()->GetFallbackSurfaceId()->is_valid());
+  EXPECT_FALSE(view_->HasFallbackSurface());
   EXPECT_EQ(gfx::Size(300, 300),
             view_->delegated_frame_host_->CurrentFrameSizeInDipForTesting());
 
@@ -3134,7 +3138,7 @@
   EXPECT_EQ(gfx::Size(400, 400),
             view_->delegated_frame_host_->CurrentFrameSizeInDipForTesting());
 
-  // Submitting a CompositorFrame should update the fallback SurfaceInfo
+  // Submitting a CompositorFrame should update the fallback SurfaceId
   view_->SubmitCompositorFrame(
       kArbitraryLocalSurfaceId,
       MakeDelegatedFrame(1.f, gfx::Size(400, 400), gfx::Rect(400, 400)),
@@ -3142,31 +3146,6 @@
   EXPECT_EQ(gfx::Size(400, 400), view_->window_->layer()->size());
 }
 
-// This test verifies that even if the primary surface is evicted after
-// the view is hidden, when it is shown again, the layer is repopulated.
-TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest, HideThenShow) {
-  view_->InitAsChild(nullptr);
-  aura::client::ParentWindowWithContext(
-      view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
-      gfx::Rect());
-
-  EXPECT_FALSE(view_->HasPrimarySurface());
-  ASSERT_TRUE(view_->delegated_frame_host_);
-
-  view_->SetSize(gfx::Size(300, 300));
-  ASSERT_TRUE(view_->HasPrimarySurface());
-  EXPECT_EQ(gfx::Size(300, 300), view_->window_->layer()->size());
-  EXPECT_EQ(gfx::Size(300, 300),
-            view_->delegated_frame_host_->CurrentFrameSizeInDipForTesting());
-
-  view_->Hide();
-  view_->delegated_frame_host_->ClearDelegatedFrame();
-  ASSERT_FALSE(view_->HasPrimarySurface());
-
-  view_->Show();
-  ASSERT_TRUE(view_->HasPrimarySurface());
-}
-
 // This test verifies that the primary SurfaceInfo is updated on device scale
 // factor changes.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
@@ -3226,9 +3205,7 @@
 
 // This test verifies that frame eviction plays well with surface
 // synchronizaton. This test is similar to
-// RenderWidgetHostViewAuraTest.DiscardDelegatedFrame but resizes instead
-// of submitting frame as that's when the primary surface is set when
-// surface synchronization is on.
+// RenderWidgetHostViewAuraTest.DiscardDelegatedFrame.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest,
        DiscardDelegatedFrames) {
   view_->InitAsChild(nullptr);
@@ -3237,6 +3214,8 @@
       FrameEvictionManager::GetInstance()->GetMaxNumberOfSavedFrames();
   ASSERT_LE(2u, max_renderer_frames);
   size_t renderer_count = max_renderer_frames + 1;
+  gfx::Rect view_rect(100, 100);
+  gfx::Size frame_size = view_rect.size();
 
   std::unique_ptr<RenderWidgetHostImpl* []> hosts(
       new RenderWidgetHostImpl*[renderer_count]);
@@ -3251,8 +3230,7 @@
                                                 process_host_, routing_id);
     delegates_.back()->set_widget_host(hosts[i]);
     hosts[i]->Init();
-    views[i] = new FakeRenderWidgetHostViewAura(
-        hosts[i], false, true /* enable_surface_synchronization */);
+    views[i] = new FakeRenderWidgetHostViewAura(hosts[i], false);
     // Prevent frames from being skipped due to resize, this test does not
     // run a UI compositor so the DelegatedFrameHost doesn't get the chance
     // to release its resize lock once it receives a frame of the expected
@@ -3262,64 +3240,101 @@
     aura::client::ParentWindowWithContext(
         views[i]->GetNativeView(),
         parent_view_->GetNativeView()->GetRootWindow(), gfx::Rect());
-    // Make each renderer visible, resize it, then make it invisible.
+    views[i]->SetSize(view_rect.size());
+    ASSERT_TRUE(views[i]->HasPrimarySurface());
+  }
+
+  // Make each renderer visible, and swap a frame on it, then make it invisible.
+  for (size_t i = 0; i < renderer_count; ++i) {
     views[i]->Show();
-    views[i]->SetSize(gfx::Size(300, 300));
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    ASSERT_TRUE(views[i]->HasPrimarySurface());
+    ASSERT_FALSE(views[i]->HasFallbackSurface());
+    views[i]->SubmitCompositorFrame(
+        kArbitraryLocalSurfaceId,
+        MakeDelegatedFrame(1.f, frame_size, view_rect), nullptr);
+    ASSERT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
     views[i]->Hide();
   }
 
   // There should be max_renderer_frames with a frame in it, and one without it.
   // Since the logic is LRU eviction, the first one should be without.
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
   for (size_t i = 1; i < renderer_count; ++i)
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
 
   // LRU renderer is [0], make it visible, it shouldn't evict anything yet.
   views[0]->Show();
-  EXPECT_TRUE(views[0]->HasPrimarySurface());
-  EXPECT_FALSE(views[1]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
+  EXPECT_TRUE(views[1]->HasFallbackSurface());
 
-  // Resize [0], it should evict the next LRU [1].
-  views[0]->SetSize(gfx::Size(300, 300));
-  EXPECT_TRUE(views[0]->HasPrimarySurface());
-  EXPECT_FALSE(views[1]->HasPrimarySurface());
+  // Swap a frame on it, it should evict the next LRU [1].
+  views[0]->SubmitCompositorFrame(
+      kArbitraryLocalSurfaceId, MakeDelegatedFrame(1.f, frame_size, view_rect),
+      nullptr);
+  EXPECT_TRUE(views[0]->HasFallbackSurface());
+  EXPECT_FALSE(views[1]->HasFallbackSurface());
   views[0]->Hide();
 
-  // LRU renderer is [1], still hidden. Resize it, it should evict
+  // LRU renderer is [1], still hidden. Swap a frame on it, it should evict
   // the next LRU [2].
-  views[1]->Show();
-  views[1]->SetSize(gfx::Size(300, 300));
-  views[1]->Hide();
-  EXPECT_TRUE(views[0]->HasPrimarySurface());
-  EXPECT_TRUE(views[1]->HasPrimarySurface());
-  EXPECT_FALSE(views[2]->HasPrimarySurface());
+  views[1]->SubmitCompositorFrame(
+      kArbitraryLocalSurfaceId, MakeDelegatedFrame(1.f, frame_size, view_rect),
+      nullptr);
+  EXPECT_TRUE(views[0]->HasFallbackSurface());
+  EXPECT_TRUE(views[1]->HasFallbackSurface());
+  EXPECT_FALSE(views[2]->HasFallbackSurface());
   for (size_t i = 3; i < renderer_count; ++i)
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
 
-  // Make all renderers but [0] visible and resize them, keep [0]
+  // Make all renderers but [0] visible and swap a frame on them, keep [0]
   // hidden, it becomes the LRU.
   for (size_t i = 1; i < renderer_count; ++i) {
     views[i]->Show();
-    views[i]->SetSize(gfx::Size(300, 300));
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    // The renderers who don't have a frame should be waiting. The ones that
+    // have a frame should not.
+    views[i]->SubmitCompositorFrame(
+        kArbitraryLocalSurfaceId,
+        MakeDelegatedFrame(1.f, frame_size, view_rect), nullptr);
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
   }
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
 
-  // Resize [0], it should be evicted immediately.
-  views[0]->SetSize(gfx::Size(300, 300));
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  // Swap a frame on [0], it should be evicted immediately.
+  views[0]->SubmitCompositorFrame(
+      kArbitraryLocalSurfaceId, MakeDelegatedFrame(1.f, frame_size, view_rect),
+      nullptr);
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
 
   // Make [0] visible, and swap a frame on it. Nothing should be evicted
   // although we're above the limit.
   views[0]->Show();
-  views[0]->SetSize(gfx::Size(300, 300));
+  views[0]->SubmitCompositorFrame(
+      kArbitraryLocalSurfaceId, MakeDelegatedFrame(1.f, frame_size, view_rect),
+      nullptr);
   for (size_t i = 0; i < renderer_count; ++i)
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
 
   // Make [0] hidden, it should evict its frame.
   views[0]->Hide();
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
+
+  // Make [0] visible, don't give it a frame, it should be waiting.
+  views[0]->Show();
+  // Make [0] hidden, it should stop waiting.
+  views[0]->Hide();
+
+  // Make [1] hidden, resize it. It should drop its frame.
+  views[1]->Hide();
+  EXPECT_TRUE(views[1]->HasFallbackSurface());
+  gfx::Size size2(200, 200);
+  viz::LocalSurfaceId id2 = parent_local_surface_id_allocator_.GenerateId();
+  views[1]->SetSize(size2);
+  EXPECT_FALSE(views[1]->HasFallbackSurface());
+  // Show it, it should block until we give it a frame.
+  views[1]->Show();
+  views[1]->SubmitCompositorFrame(
+      id2, MakeDelegatedFrame(1.f, size2, gfx::Rect(size2)), nullptr);
 
   for (size_t i = 0; i < renderer_count; ++i) {
     views[i]->Destroy();
@@ -3350,8 +3365,7 @@
                                                 process_host_, routing_id);
     delegates_.back()->set_widget_host(hosts[i]);
     hosts[i]->Init();
-    views[i] = new FakeRenderWidgetHostViewAura(
-        hosts[i], false, false /* enable_surface_synchronization */);
+    views[i] = new FakeRenderWidgetHostViewAura(hosts[i], false);
     // Prevent frames from being skipped due to resize, this test does not
     // run a UI compositor so the DelegatedFrameHost doesn't get the chance
     // to release its resize lock once it receives a frame of the expected
@@ -3371,20 +3385,20 @@
     views[i]->SubmitCompositorFrame(
         kArbitraryLocalSurfaceId,
         MakeDelegatedFrame(1.f, frame_size, view_rect), nullptr);
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
     views[i]->Hide();
   }
 
   // There should be max_renderer_frames with a frame in it, and one without it.
   // Since the logic is LRU eviction, the first one should be without.
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
   for (size_t i = 1; i < renderer_count; ++i)
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
 
   // LRU renderer is [0], make it visible, it shouldn't evict anything yet.
   views[0]->Show();
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
-  EXPECT_TRUE(views[1]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
+  EXPECT_TRUE(views[1]->HasFallbackSurface());
   // Since [0] doesn't have a frame, it should be waiting for the renderer to
   // give it one.
   EXPECT_TRUE(views[0]->released_front_lock_active());
@@ -3393,8 +3407,8 @@
   views[0]->SubmitCompositorFrame(
       kArbitraryLocalSurfaceId, MakeDelegatedFrame(1.f, frame_size, view_rect),
       nullptr);
-  EXPECT_TRUE(views[0]->HasPrimarySurface());
-  EXPECT_FALSE(views[1]->HasPrimarySurface());
+  EXPECT_TRUE(views[0]->HasFallbackSurface());
+  EXPECT_FALSE(views[1]->HasFallbackSurface());
   // Now that [0] got a frame, it shouldn't be waiting any more.
   EXPECT_FALSE(views[0]->released_front_lock_active());
   views[0]->Hide();
@@ -3404,11 +3418,11 @@
   views[1]->SubmitCompositorFrame(
       kArbitraryLocalSurfaceId, MakeDelegatedFrame(1.f, frame_size, view_rect),
       nullptr);
-  EXPECT_TRUE(views[0]->HasPrimarySurface());
-  EXPECT_TRUE(views[1]->HasPrimarySurface());
-  EXPECT_FALSE(views[2]->HasPrimarySurface());
+  EXPECT_TRUE(views[0]->HasFallbackSurface());
+  EXPECT_TRUE(views[1]->HasFallbackSurface());
+  EXPECT_FALSE(views[2]->HasFallbackSurface());
   for (size_t i = 3; i < renderer_count; ++i)
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
 
   // Make all renderers but [0] visible and swap a frame on them, keep [0]
   // hidden, it becomes the LRU.
@@ -3417,22 +3431,22 @@
     // The renderers who don't have a frame should be waiting. The ones that
     // have a frame should not.
     // In practice, [1] has a frame, but anything after has its frame evicted.
-    EXPECT_EQ(!views[i]->HasPrimarySurface(),
+    EXPECT_EQ(!views[i]->HasFallbackSurface(),
               views[i]->released_front_lock_active());
     views[i]->SubmitCompositorFrame(
         kArbitraryLocalSurfaceId,
         MakeDelegatedFrame(1.f, frame_size, view_rect), nullptr);
     // Now everyone has a frame.
     EXPECT_FALSE(views[i]->released_front_lock_active());
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
   }
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
 
   // Swap a frame on [0], it should be evicted immediately.
   views[0]->SubmitCompositorFrame(
       kArbitraryLocalSurfaceId, MakeDelegatedFrame(1.f, frame_size, view_rect),
       nullptr);
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
 
   // Make [0] visible, and swap a frame on it. Nothing should be evicted
   // although we're above the limit.
@@ -3444,11 +3458,11 @@
       nullptr);
   EXPECT_FALSE(views[0]->released_front_lock_active());
   for (size_t i = 0; i < renderer_count; ++i)
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
 
   // Make [0] hidden, it should evict its frame.
   views[0]->Hide();
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
 
   // Make [0] visible, don't give it a frame, it should be waiting.
   views[0]->Show();
@@ -3459,11 +3473,11 @@
 
   // Make [1] hidden, resize it. It should drop its frame.
   views[1]->Hide();
-  EXPECT_TRUE(views[1]->HasPrimarySurface());
+  EXPECT_TRUE(views[1]->HasFallbackSurface());
   gfx::Size size2(200, 200);
   viz::LocalSurfaceId id2 = parent_local_surface_id_allocator_.GenerateId();
   views[1]->SetSize(size2);
-  EXPECT_FALSE(views[1]->HasPrimarySurface());
+  EXPECT_FALSE(views[1]->HasFallbackSurface());
   // Show it, it should block until we give it a frame.
   views[1]->Show();
   EXPECT_TRUE(views[1]->released_front_lock_active());
@@ -3500,8 +3514,7 @@
                                                 process_host_, routing_id);
     delegates_.back()->set_widget_host(hosts[i]);
     hosts[i]->Init();
-    views[i] = new FakeRenderWidgetHostViewAura(
-        hosts[i], false, false /* enable_surface_synchronization */);
+    views[i] = new FakeRenderWidgetHostViewAura(hosts[i], false);
     views[i]->InitAsChild(nullptr);
     aura::client::ParentWindowWithContext(
         views[i]->GetNativeView(),
@@ -3518,26 +3531,26 @@
         i ? parent_local_surface_id_allocator_.GenerateId()
           : kArbitraryLocalSurfaceId,
         MakeDelegatedFrame(1.f, frame_size, view_rect), nullptr);
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
   }
 
   // If we hide [0], then [0] should be evicted.
   views[0]->Hide();
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
 
   // If we lock [0] before hiding it, then [0] should not be evicted.
   views[0]->Show();
   views[0]->SubmitCompositorFrame(
       kArbitraryLocalSurfaceId, MakeDelegatedFrame(1.f, frame_size, view_rect),
       nullptr);
-  EXPECT_TRUE(views[0]->HasPrimarySurface());
+  EXPECT_TRUE(views[0]->HasFallbackSurface());
   views[0]->GetDelegatedFrameHost()->LockResources();
   views[0]->Hide();
-  EXPECT_TRUE(views[0]->HasPrimarySurface());
+  EXPECT_TRUE(views[0]->HasFallbackSurface());
 
   // If we unlock [0] now, then [0] should be evicted.
   views[0]->GetDelegatedFrameHost()->UnlockResources();
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
 
   for (size_t i = 0; i < renderer_count; ++i) {
     views[i]->Destroy();
@@ -3575,8 +3588,7 @@
                                                 process_host_, routing_id);
     delegates_.back()->set_widget_host(hosts[i]);
     hosts[i]->Init();
-    views[i] = new FakeRenderWidgetHostViewAura(
-        hosts[i], false, false /* enable_surface_synchronization */);
+    views[i] = new FakeRenderWidgetHostViewAura(hosts[i], false);
     views[i]->InitAsChild(nullptr);
     aura::client::ParentWindowWithContext(
         views[i]->GetNativeView(),
@@ -3592,27 +3604,27 @@
     views[i]->SubmitCompositorFrame(
         kArbitraryLocalSurfaceId,
         MakeDelegatedFrame(1.f, frame_size, view_rect), nullptr);
-    EXPECT_TRUE(views[i]->HasPrimarySurface());
+    EXPECT_TRUE(views[i]->HasFallbackSurface());
   }
 
   // If we hide one, it should not get evicted.
   views[0]->Hide();
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(views[0]->HasPrimarySurface());
+  EXPECT_TRUE(views[0]->HasFallbackSurface());
   // Using a lesser memory pressure event however, should evict.
   SimulateMemoryPressure(
       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(views[0]->HasPrimarySurface());
+  EXPECT_FALSE(views[0]->HasFallbackSurface());
 
   // Check the same for a higher pressure event.
   views[1]->Hide();
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(views[1]->HasPrimarySurface());
+  EXPECT_TRUE(views[1]->HasFallbackSurface());
   SimulateMemoryPressure(
       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(views[1]->HasPrimarySurface());
+  EXPECT_FALSE(views[1]->HasFallbackSurface());
 
   for (size_t i = 0; i < renderer_count; ++i) {
     views[i]->Destroy();
@@ -6029,7 +6041,6 @@
     // This instance is destroyed in the TearDown method below.
     view_ = new RenderWidgetHostViewAura(
         contents()->GetRenderViewHost()->GetWidget(), false,
-        false /* enable_surface_synchronization */,
         false /* is_mus_browser_plugin_guest */);
   }
 
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index ceafaba..7d17c87 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -545,7 +545,6 @@
       completed_overscroll_gesture_(OVERSCROLL_NONE),
       navigation_overlay_(nullptr),
       init_rwhv_with_null_parent_for_testing_(false) {
-  enable_surface_synchronization_ = features::IsSurfaceSynchronizationEnabled();
 }
 
 void WebContentsViewAura::SetDelegateForTesting(
@@ -905,7 +904,6 @@
           ? g_create_render_widget_host_view(render_widget_host,
                                              is_guest_view_hack)
           : new RenderWidgetHostViewAura(render_widget_host, is_guest_view_hack,
-                                         enable_surface_synchronization_,
                                          is_mus_browser_plugin_guest_);
   view->InitAsChild(GetRenderWidgetHostViewParent());
 
@@ -936,7 +934,6 @@
   // |is_mus_browser_plugin_guest| is always false for them.
   const bool is_mus_browser_plugin_guest = false;
   return new RenderWidgetHostViewAura(render_widget_host, false,
-                                      enable_surface_synchronization_,
                                       is_mus_browser_plugin_guest);
 }
 
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index 7b2078f5..3e0cf1b 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -201,8 +201,6 @@
 
   const bool is_mus_browser_plugin_guest_;
 
-  bool enable_surface_synchronization_ = false;
-
   // NOTE: this is null when running in mus and |is_mus_browser_plugin_guest_|.
   std::unique_ptr<aura::Window> window_;
 
diff --git a/content/child/OWNERS b/content/child/OWNERS
index 2029635..e9510bd4 100644
--- a/content/child/OWNERS
+++ b/content/child/OWNERS
@@ -1,8 +1,5 @@
 haraken@chromium.org
 
-# For Blink API usage
-dglazkov@chromium.org
-
 # AppCache
 per-file appcache*=michaeln@chromium.org
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index fb1f64a..01c869c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -228,7 +228,13 @@
 
 // Whether a download can be handled by parallel jobs.
 const base::Feature kParallelDownloading{
-    "ParallelDownloading", base::FEATURE_DISABLED_BY_DEFAULT};
+  "ParallelDownloading",
+#if defined(OS_ANDROID)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
 
 // Whether document level event listeners should default 'passive' to true.
 const base::Feature kPassiveDocumentEventListeners{
diff --git a/content/renderer/OWNERS b/content/renderer/OWNERS
index 91c0867c..c367d110 100644
--- a/content/renderer/OWNERS
+++ b/content/renderer/OWNERS
@@ -1,8 +1,5 @@
 haraken@chromium.org
 
-# For Blink API usage
-dglazkov@chromium.org
-
 # Mac Sandbox profiles.
 per-file *.sb=set noparent
 per-file *.sb=rsesek@chromium.org
diff --git a/content/shell/renderer/layout_test/layout_test_content_renderer_client.cc b/content/shell/renderer/layout_test/layout_test_content_renderer_client.cc
index 96b3648..a1f790e 100644
--- a/content/shell/renderer/layout_test/layout_test_content_renderer_client.cc
+++ b/content/shell/renderer/layout_test/layout_test_content_renderer_client.cc
@@ -30,6 +30,7 @@
 #include "content/shell/test_runner/web_test_runner.h"
 #include "content/shell/test_runner/web_view_test_proxy.h"
 #include "content/test/mock_webclipboard_impl.h"
+#include "gin/modules/module_registry.h"
 #include "media/base/audio_latency.h"
 #include "media/base/mime_util.h"
 #include "media/media_features.h"
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 1144edb..03053e04 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -251,6 +251,8 @@
 
   if (enable_vr && is_android) {
     sources += [
+      "vr/orientation/orientation_device_provider_unittest.cc",
+      "vr/orientation/orientation_device_unittest.cc",
       "vr/vr_device_base_unittest.cc",
       "vr/vr_display_impl_unittest.cc",
     ]
@@ -260,6 +262,8 @@
       "//device/vr:fakes",
       "//device/vr:java",
       "//device/vr:mojo_bindings",
+      "//services/device/public/cpp/generic_sensor",
+      "//ui/display",
     ]
   }
 }
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index 27081d6..05ee861 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -22,6 +22,10 @@
       "features",
     ]
     sources += [
+      "orientation/orientation_device.cc",
+      "orientation/orientation_device.h",
+      "orientation/orientation_device_provider.cc",
+      "orientation/orientation_device_provider.h",
       "vr_device.h",
       "vr_device_base.cc",
       "vr_device_base.h",
@@ -34,6 +38,9 @@
       "//base",
       "//gpu/ipc/common:interfaces",
       "//mojo/public/cpp/bindings",
+      "//services/device/public/cpp/generic_sensor",
+      "//services/service_manager/public/cpp",
+      "//ui/display",
       "//ui/gfx",
     ]
 
@@ -110,6 +117,10 @@
     defines = [ "DEVICE_VR_IMPLEMENTATION" ]
 
     sources = [
+      "test/fake_orientation_provider.cc",
+      "test/fake_orientation_provider.h",
+      "test/fake_sensor_provider.cc",
+      "test/fake_sensor_provider.h",
       "test/fake_vr_device.cc",
       "test/fake_vr_device.h",
       "test/fake_vr_device_provider.cc",
@@ -128,6 +139,7 @@
       ":vr",
       "//base",
       "//mojo/public/cpp/bindings",
+      "//services/device/public/cpp/generic_sensor",
       "//testing/gmock",
     ]
   }
diff --git a/device/vr/DEPS b/device/vr/DEPS
index eca72cd..5714690 100644
--- a/device/vr/DEPS
+++ b/device/vr/DEPS
@@ -2,8 +2,10 @@
   "+device/gamepad/public/cpp/gamepads.h",
   "+gpu/command_buffer/common/mailbox_holder.h",
   "+jni",
+  "+services/device/public",
   "+third_party/WebKit/public/platform/modules/vr/vr_service.mojom.h",
   "+third_party/gvr-android-sdk/src",
   "+third_party/openvr/src/headers/openvr.h",
+  "+ui/display",
   "+ui/gfx",
 ]
diff --git a/device/vr/orientation/orientation_device.cc b/device/vr/orientation/orientation_device.cc
new file mode 100644
index 0000000..3913108
--- /dev/null
+++ b/device/vr/orientation/orientation_device.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <math.h>
+
+#include "base/memory/ptr_util.h"
+#include "base/numerics/math_constants.h"
+#include "base/time/time.h"
+#include "device/vr/orientation/orientation_device.h"
+#include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
+#include "services/device/public/interfaces/sensor_provider.mojom.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace device {
+
+using mojom::SensorType;
+using gfx::Quaternion;
+using gfx::Vector3dF;
+
+namespace {
+static constexpr int kDefaultPumpFrequencyHz = 60;
+
+mojom::VRDisplayInfoPtr CreateVRDisplayInfo(unsigned int id) {
+  static const char DEVICE_NAME[] = "VR Orientation Device";
+
+  mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
+  display_info->index = id;
+  display_info->displayName = DEVICE_NAME;
+  display_info->capabilities = mojom::VRDisplayCapabilities::New();
+  display_info->capabilities->hasPosition = false;
+  display_info->capabilities->hasExternalDisplay = false;
+  display_info->capabilities->canPresent = false;
+
+  return display_info;
+}
+
+display::Display::Rotation GetRotation() {
+  display::Screen* screen = display::Screen::GetScreen();
+  if (!screen) {
+    // If we can't get rotation we'll assume it's 0.
+    return display::Display::ROTATE_0;
+  }
+
+  return screen->GetPrimaryDisplay().rotation();
+}
+
+}  // namespace
+
+VROrientationDevice::VROrientationDevice(
+    mojom::SensorProviderPtr* sensor_provider,
+    base::OnceClosure ready_callback)
+    : ready_callback_(std::move(ready_callback)), binding_(this) {
+  (*sensor_provider)
+      ->GetSensor(SensorType::ABSOLUTE_ORIENTATION_QUATERNION,
+                  base::BindOnce(&VROrientationDevice::SensorReady,
+                                 base::Unretained(this)));
+
+  SetVRDisplayInfo(CreateVRDisplayInfo(GetId()));
+}
+
+VROrientationDevice::~VROrientationDevice() = default;
+
+void VROrientationDevice::SensorReady(
+    device::mojom::SensorInitParamsPtr params) {
+  if (!params) {
+    // This means that there are no orientation sensors on this device.
+    HandleSensorError();
+    std::move(ready_callback_).Run();
+    return;
+  }
+
+  constexpr size_t kReadBufferSize = sizeof(device::SensorReadingSharedBuffer);
+
+  DCHECK_EQ(0u, params->buffer_offset % kReadBufferSize);
+
+  device::PlatformSensorConfiguration default_config =
+      params->default_configuration;
+
+  sensor_.Bind(std::move(params->sensor));
+
+  binding_.Bind(std::move(params->client_request));
+
+  shared_buffer_handle_ = std::move(params->memory);
+  DCHECK(!shared_buffer_);
+  shared_buffer_ = shared_buffer_handle_->MapAtOffset(kReadBufferSize,
+                                                      params->buffer_offset);
+
+  if (!shared_buffer_) {
+    // If we cannot read data, we cannot supply a device.
+    HandleSensorError();
+    std::move(ready_callback_).Run();
+    return;
+  }
+
+  const device::SensorReadingSharedBuffer* buffer =
+      static_cast<const device::SensorReadingSharedBuffer*>(
+          shared_buffer_.get());
+  shared_buffer_reader_.reset(
+      new device::SensorReadingSharedBufferReader(buffer));
+
+  default_config.set_frequency(kDefaultPumpFrequencyHz);
+  sensor_.set_connection_error_handler(base::BindOnce(
+      &VROrientationDevice::HandleSensorError, base::Unretained(this)));
+  sensor_->ConfigureReadingChangeNotifications(false /* disabled */);
+  sensor_->AddConfiguration(
+      default_config,
+      base::BindOnce(&VROrientationDevice::OnSensorAddConfiguration,
+                     base::Unretained(this)));
+}
+
+// Mojo callback for Sensor::AddConfiguration().
+void VROrientationDevice::OnSensorAddConfiguration(bool success) {
+  if (!success) {
+    // Sensor config is not supported so we can't provide sensor events.
+    HandleSensorError();
+  } else {
+    // We're good to go.
+    available_ = true;
+  }
+
+  std::move(ready_callback_).Run();
+}
+
+void VROrientationDevice::RaiseError() {
+  HandleSensorError();
+}
+
+void VROrientationDevice::HandleSensorError() {
+  sensor_.reset();
+  shared_buffer_handle_.reset();
+  shared_buffer_.reset();
+  binding_.Close();
+}
+
+void VROrientationDevice::OnMagicWindowPoseRequest(
+    mojom::VRMagicWindowProvider::GetPoseCallback callback) {
+  mojom::VRPosePtr pose = mojom::VRPose::New();
+  pose->orientation.emplace(4);
+
+  SensorReading latest_reading;
+  // If the reading fails just return the last pose that we got.
+  if (shared_buffer_reader_->GetReading(&latest_reading)) {
+    latest_pose_.set_x(latest_reading.orientation_quat.x);
+    latest_pose_.set_y(latest_reading.orientation_quat.y);
+    latest_pose_.set_z(latest_reading.orientation_quat.z);
+    latest_pose_.set_w(latest_reading.orientation_quat.w);
+
+    latest_pose_ =
+        WorldSpaceToUserOrientedSpace(SensorSpaceToWorldSpace(latest_pose_));
+  }
+
+  pose->orientation.value()[0] = latest_pose_.x();
+  pose->orientation.value()[1] = latest_pose_.y();
+  pose->orientation.value()[2] = latest_pose_.z();
+  pose->orientation.value()[3] = latest_pose_.w();
+
+  std::move(callback).Run(std::move(pose));
+}
+
+Quaternion VROrientationDevice::SensorSpaceToWorldSpace(Quaternion q) {
+  display::Display::Rotation rotation = GetRotation();
+
+  if (rotation == display::Display::ROTATE_90) {
+    // Rotate the sensor reading to account for the screen rotation.
+    q = q * Quaternion(Vector3dF(0, 0, 1), -base::kPiDouble / 2);
+  } else if (rotation == display::Display::ROTATE_270) {
+    // Rotate the sensor reading to account for the screen rotation the other
+    // way.
+    q = q * Quaternion(Vector3dF(0, 0, 1), base::kPiDouble / 2);
+  }
+
+  // Tilt the view up to have the y axis as the vertical axis instead of z
+  q = Quaternion(Vector3dF(1, 0, 0), -base::kPiDouble / 2) * q;
+
+  return q;
+}
+
+Quaternion VROrientationDevice::WorldSpaceToUserOrientedSpace(Quaternion q) {
+  if (!base_pose_) {
+    // Check that q is valid by checking if the length is not 0 (it should
+    // technically always be 1, but this accounts for rounding errors).
+    if (!(q.Length() > .1)) {
+      // q is invalid. Do not use for base pose.
+      return q;
+    }
+
+    // A base pose to read the initial forward direction off of.
+    base_pose_ = q;
+
+    // Extract the yaw from base pose to use as the base forward direction.
+    base_pose_->set_x(0);
+    base_pose_->set_z(0);
+    base_pose_ = base_pose_->Normalized();
+  }
+
+  // Adjust the base forward on the orientation to where the original forward
+  // was.
+  q = base_pose_->inverse() * q;
+
+  return q;
+}
+
+bool VROrientationDevice::IsFallbackDevice() {
+  return true;
+};
+
+}  // namespace device
\ No newline at end of file
diff --git a/device/vr/orientation/orientation_device.h b/device/vr/orientation/orientation_device.h
new file mode 100644
index 0000000..8090c48d
--- /dev/null
+++ b/device/vr/orientation/orientation_device.h
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_VR_ORIENTATION_DEVICE_H
+#define DEVICE_VR_ORIENTATION_DEVICE_H
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/threading/simple_thread.h"
+#include "device/vr/vr_device_base.h"
+#include "device/vr/vr_service.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/device/public/interfaces/sensor_provider.mojom.h"
+#include "ui/gfx/geometry/quaternion.h"
+
+namespace device {
+
+class SensorReadingSharedBufferReader;
+
+// This class connects the orientation sensor events to the Web VR apis.
+class DEVICE_VR_EXPORT VROrientationDevice : public VRDeviceBase,
+                                             public mojom::SensorClient {
+ public:
+  VROrientationDevice(mojom::SensorProviderPtr* sensor_provider,
+                      base::OnceClosure ready_callback);
+  ~VROrientationDevice() override;
+
+  // VRDeviceBase
+  void OnMagicWindowPoseRequest(
+      mojom::VRMagicWindowProvider::GetPoseCallback callback) override;
+
+  bool IsFallbackDevice() override;
+
+  // Indicates whether the device was able to connect to orientation events.
+  bool IsAvailable() const { return available_; }
+
+ private:
+  // SensorClient Functions.
+  void RaiseError() override;
+  void SensorReadingChanged() override {}
+
+  // Sensor event reaction functions.
+  void SensorReady(device::mojom::SensorInitParamsPtr params);
+  void HandleSensorError();
+  void OnSensorAddConfiguration(bool success);
+
+  gfx::Quaternion SensorSpaceToWorldSpace(gfx::Quaternion q);
+  gfx::Quaternion WorldSpaceToUserOrientedSpace(gfx::Quaternion q);
+
+  bool available_ = false;
+  base::OnceClosure ready_callback_;
+
+  // The initial state of the world used to define forwards.
+  base::Optional<gfx::Quaternion> base_pose_;
+  gfx::Quaternion latest_pose_;
+
+  mojom::SensorPtr sensor_;
+  mojo::ScopedSharedBufferHandle shared_buffer_handle_;
+  mojo::ScopedSharedBufferMapping shared_buffer_;
+  std::unique_ptr<SensorReadingSharedBufferReader> shared_buffer_reader_;
+  mojo::Binding<mojom::SensorClient> binding_;
+};
+
+}  // namespace device
+
+#endif  // DEVICE_VR_ORIENTATION_DEVICE_H
diff --git a/device/vr/orientation/orientation_device_provider.cc b/device/vr/orientation/orientation_device_provider.cc
new file mode 100644
index 0000000..ff733d85
--- /dev/null
+++ b/device/vr/orientation/orientation_device_provider.cc
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/vr/orientation/orientation_device_provider.h"
+
+#include "base/callback.h"
+#include "device/vr/orientation/orientation_device.h"
+#include "services/device/public/interfaces/sensor_provider.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/identity.h"
+
+namespace device {
+
+VROrientationDeviceProvider::VROrientationDeviceProvider(
+    service_manager::Connector* connector) {
+  connector->BindInterface(device::mojom::kServiceName,
+                           mojo::MakeRequest(&sensor_provider_));
+}
+
+VROrientationDeviceProvider::~VROrientationDeviceProvider() = default;
+
+void VROrientationDeviceProvider::Initialize(
+    base::RepeatingCallback<void(VRDevice*)> add_device_callback,
+    base::RepeatingCallback<void(VRDevice*)> remove_device_callback,
+    base::OnceClosure initialization_complete) {
+  if (device_ && device_->IsAvailable()) {
+    add_device_callback.Run(device_.get());
+    return;
+  }
+
+  if (!device_) {
+    device_ = std::make_unique<VROrientationDevice>(
+        &sensor_provider_,
+        base::BindOnce(&VROrientationDeviceProvider::DeviceInitialized,
+                       base::Unretained(this)));
+    add_device_callback_ = add_device_callback;
+    initialized_callback_ = std::move(initialization_complete);
+  }
+}
+
+bool VROrientationDeviceProvider::Initialized() {
+  return initialized_;
+};
+
+void VROrientationDeviceProvider::DeviceInitialized() {
+  // This should only be called after the device is initialized.
+  DCHECK(device_);
+  // This should only be called once.
+  DCHECK(!initialized_);
+
+  // If the device successfully connected to the orientation APIs, provide it.
+  if (device_->IsAvailable()) {
+    add_device_callback_.Run(device_.get());
+  }
+
+  initialized_ = true;
+  std::move(initialized_callback_).Run();
+}
+
+}  // namespace device
diff --git a/device/vr/orientation/orientation_device_provider.h b/device/vr/orientation/orientation_device_provider.h
new file mode 100644
index 0000000..f9852dc
--- /dev/null
+++ b/device/vr/orientation/orientation_device_provider.h
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_VR_ORIENTATION_DEVICE_PROVIDER_H
+#define DEVICE_VR_ORIENTATION_DEVICE_PROVIDER_H
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "device/vr/orientation/orientation_device.h"
+#include "device/vr/vr_device_provider.h"
+#include "device/vr/vr_export.h"
+#include "services/device/public/interfaces/constants.mojom.h"
+#include "services/device/public/interfaces/sensor_provider.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace device {
+
+class DEVICE_VR_EXPORT VROrientationDeviceProvider : public VRDeviceProvider {
+ public:
+  VROrientationDeviceProvider(service_manager::Connector* connector);
+  ~VROrientationDeviceProvider() override;
+
+  void Initialize(
+      base::RepeatingCallback<void(VRDevice*)> add_device_callback,
+      base::RepeatingCallback<void(VRDevice*)> remove_device_callback,
+      base::OnceClosure initialization_complete) override;
+
+  bool Initialized() override;
+
+ private:
+  void DeviceInitialized();
+
+  bool initialized_ = false;
+
+  device::mojom::SensorProviderPtr sensor_provider_;
+
+  std::unique_ptr<VROrientationDevice> device_;
+
+  base::RepeatingCallback<void(VRDevice*)> add_device_callback_;
+  base::OnceClosure initialized_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(VROrientationDeviceProvider);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_VR_ORIENTATION_DEVICE_PROVIDER_H
diff --git a/device/vr/orientation/orientation_device_provider_unittest.cc b/device/vr/orientation/orientation_device_provider_unittest.cc
new file mode 100644
index 0000000..1c7a30d
--- /dev/null
+++ b/device/vr/orientation/orientation_device_provider_unittest.cc
@@ -0,0 +1,207 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "device/vr/orientation/orientation_device.h"
+#include "device/vr/orientation/orientation_device_provider.h"
+#include "device/vr/test/fake_orientation_provider.h"
+#include "device/vr/test/fake_sensor_provider.h"
+#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
+#include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
+#include "services/device/public/cpp/generic_sensor/sensor_traits.h"
+#include "services/device/public/interfaces/sensor.mojom.h"
+#include "services/device/public/interfaces/sensor_provider.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/quaternion.h"
+
+namespace device {
+
+class VROrientationDeviceProviderTest : public testing::Test {
+ protected:
+  VROrientationDeviceProviderTest() = default;
+  ~VROrientationDeviceProviderTest() override = default;
+  void SetUp() override {
+    fake_sensor_provider_ = std::make_unique<FakeSensorProvider>();
+
+    fake_sensor_ = std::make_unique<FakeOrientationSensor>(
+        mojo::MakeRequest(&sensor_ptr_));
+    shared_buffer_handle_ = mojo::SharedBufferHandle::Create(
+        sizeof(SensorReadingSharedBuffer) *
+        static_cast<uint64_t>(mojom::SensorType::LAST));
+
+    service_manager::mojom::ConnectorRequest request;
+    connector_ = service_manager::Connector::Create(&request);
+    service_manager::Connector::TestApi test_api(connector_.get());
+    test_api.OverrideBinderForTesting(
+        mojom::kServiceName, mojom::SensorProvider::Name_,
+        base::BindRepeating(&FakeSensorProvider::Bind,
+                            base::Unretained(fake_sensor_provider_.get())));
+
+    provider_ = std::make_unique<VROrientationDeviceProvider>(connector_.get());
+
+    scoped_task_environment_.RunUntilIdle();
+  }
+
+  void TearDown() override {}
+
+  void InitializeDevice(mojom::SensorInitParamsPtr params) {
+    // Be sure GetSensor goes through so the callback is set.
+    scoped_task_environment_.RunUntilIdle();
+
+    fake_sensor_provider_->CallCallback(std::move(params));
+
+    // Allow the callback call to go through.
+    scoped_task_environment_.RunUntilIdle();
+  }
+
+  mojom::SensorInitParamsPtr FakeInitParams() {
+    auto init_params = mojom::SensorInitParams::New();
+    init_params->sensor = std::move(sensor_ptr_);
+    init_params->default_configuration = PlatformSensorConfiguration(
+        SensorTraits<mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION>::
+            kDefaultFrequency);
+
+    init_params->client_request = mojo::MakeRequest(&sensor_client_ptr_);
+
+    init_params->memory = shared_buffer_handle_->Clone(
+        mojo::SharedBufferHandle::AccessMode::READ_ONLY);
+
+    init_params->buffer_offset = SensorReadingSharedBuffer::GetOffset(
+        mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION);
+
+    return init_params;
+  }
+
+  base::RepeatingCallback<void(VRDevice*)> DeviceCallbackFailIfCalled() {
+    return base::BindRepeating([](VRDevice* device) { FAIL(); });
+  };
+
+  base::RepeatingCallback<void(VRDevice*)> DeviceCallbackMustBeCalled(
+      base::RunLoop* loop) {
+    return base::BindRepeating(
+        [](base::OnceClosure quit_closure, VRDevice* device) {
+          ASSERT_TRUE(device);
+          std::move(quit_closure).Run();
+        },
+        loop->QuitClosure());
+  };
+
+  base::OnceClosure ClosureFailIfCalled() {
+    return base::BindOnce([]() { FAIL(); });
+  };
+
+  base::OnceClosure ClosureMustBeCalled(base::RunLoop* loop) {
+    return base::BindOnce(
+        [](base::OnceClosure quit_closure) { std::move(quit_closure).Run(); },
+        loop->QuitClosure());
+  };
+
+  // Needed for MakeRequest to work.
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  std::unique_ptr<VROrientationDeviceProvider> provider_;
+
+  std::unique_ptr<FakeSensorProvider> fake_sensor_provider_;
+  mojom::SensorProviderPtr sensor_provider_ptr_;
+
+  // Fake Sensor Init params objects
+  std::unique_ptr<FakeOrientationSensor> fake_sensor_;
+  mojom::SensorPtrInfo sensor_ptr_;
+  mojo::ScopedSharedBufferHandle shared_buffer_handle_;
+  mojom::SensorClientPtr sensor_client_ptr_;
+
+  std::unique_ptr<service_manager::Connector> connector_;
+
+  DISALLOW_COPY_AND_ASSIGN(VROrientationDeviceProviderTest);
+};
+
+TEST_F(VROrientationDeviceProviderTest, InitializationTest) {
+  // Check that without running anything, the provider will not be initialized.
+  EXPECT_FALSE(provider_->Initialized());
+}
+
+TEST_F(VROrientationDeviceProviderTest, InitializationCallbackSuccessTest) {
+  base::RunLoop wait_for_device;
+  base::RunLoop wait_for_init;
+
+  provider_->Initialize(DeviceCallbackMustBeCalled(&wait_for_device),
+                        DeviceCallbackFailIfCalled(),
+                        ClosureMustBeCalled(&wait_for_init));
+
+  InitializeDevice(FakeInitParams());
+
+  wait_for_init.Run();
+  wait_for_device.Run();
+
+  EXPECT_TRUE(provider_->Initialized());
+}
+
+TEST_F(VROrientationDeviceProviderTest, InitializationCallbackFailureTest) {
+  base::RunLoop wait_for_init;
+
+  provider_->Initialize(DeviceCallbackFailIfCalled(),
+                        DeviceCallbackFailIfCalled(),
+                        ClosureMustBeCalled(&wait_for_init));
+
+  InitializeDevice(nullptr);
+
+  // Wait for the initialization to finish.
+  wait_for_init.Run();
+  EXPECT_TRUE(provider_->Initialized());
+}
+
+TEST_F(VROrientationDeviceProviderTest, SecondInitializationSuccessTest) {
+  base::RunLoop wait_for_device;
+  base::RunLoop wait_for_init;
+
+  provider_->Initialize(DeviceCallbackMustBeCalled(&wait_for_device),
+                        DeviceCallbackFailIfCalled(),
+                        ClosureMustBeCalled(&wait_for_init));
+
+  InitializeDevice(FakeInitParams());
+
+  // Wait for the initialization to finish.
+  wait_for_init.Run();
+  wait_for_device.Run();
+
+  base::RunLoop second_wait_for_device;
+
+  EXPECT_TRUE(provider_->Initialized());
+
+  // If we run initialize again, we should only call add device.
+  provider_->Initialize(DeviceCallbackMustBeCalled(&second_wait_for_device),
+                        DeviceCallbackFailIfCalled(), ClosureFailIfCalled());
+
+  second_wait_for_device.Run();
+}
+
+TEST_F(VROrientationDeviceProviderTest, SecondInitializationFailureTest) {
+  base::RunLoop wait_for_init;
+
+  provider_->Initialize(DeviceCallbackFailIfCalled(),
+                        DeviceCallbackFailIfCalled(),
+                        ClosureMustBeCalled(&wait_for_init));
+
+  InitializeDevice(nullptr);
+
+  wait_for_init.Run();
+
+  EXPECT_TRUE(provider_->Initialized());
+
+  // If we call again on a failure, nothing should be called.
+  provider_->Initialize(DeviceCallbackFailIfCalled(),
+                        DeviceCallbackFailIfCalled(), ClosureFailIfCalled());
+}
+
+}  // namespace device
diff --git a/device/vr/orientation/orientation_device_unittest.cc b/device/vr/orientation/orientation_device_unittest.cc
new file mode 100644
index 0000000..b9a23d4
--- /dev/null
+++ b/device/vr/orientation/orientation_device_unittest.cc
@@ -0,0 +1,338 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "device/vr/orientation/orientation_device.h"
+#include "device/vr/test/fake_orientation_provider.h"
+#include "device/vr/test/fake_sensor_provider.h"
+#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
+#include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
+#include "services/device/public/cpp/generic_sensor/sensor_traits.h"
+#include "services/device/public/interfaces/sensor.mojom.h"
+#include "services/device/public/interfaces/sensor_provider.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/quaternion.h"
+
+namespace device {
+
+namespace {
+
+class FakeScreen : public display::Screen {
+ public:
+  FakeScreen() = default;
+  ~FakeScreen() override = default;
+  display::Display GetPrimaryDisplay() const override { return display; };
+
+  // Unused functions
+  gfx::Point GetCursorScreenPoint() override { return gfx::Point(); };
+  bool IsWindowUnderCursor(gfx::NativeWindow window) override { return false; };
+  gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override {
+    return nullptr;
+  }
+  display::Display GetDisplayNearestWindow(
+      gfx::NativeWindow window) const override {
+    return display;
+  }
+  display::Display GetDisplayNearestPoint(
+      const gfx::Point& point) const override {
+    return display;
+  }
+  int GetNumDisplays() const override { return 0; };
+  display::Display GetDisplayMatching(
+      const gfx::Rect& match_rect) const override {
+    return display;
+  }
+  void AddObserver(display::DisplayObserver* observer) override {}
+  void RemoveObserver(display::DisplayObserver* observer) override {}
+  const std::vector<display::Display>& GetAllDisplays() const override {
+    return displays;
+  }
+
+  display::Display display;
+  const std::vector<display::Display> displays;
+};
+
+}  // namespace
+
+class VROrientationDeviceTest : public testing::Test {
+ public:
+  void onDisplaySynced() {}
+
+ protected:
+  VROrientationDeviceTest() = default;
+  ~VROrientationDeviceTest() override = default;
+  void SetUp() override {
+    fake_sensor_provider_ = std::make_unique<FakeSensorProvider>(
+        mojo::MakeRequest(&sensor_provider_ptr_));
+
+    fake_sensor_ = std::make_unique<FakeOrientationSensor>(
+        mojo::MakeRequest(&sensor_ptr_));
+    shared_buffer_handle_ = mojo::SharedBufferHandle::Create(
+        sizeof(SensorReadingSharedBuffer) *
+        static_cast<uint64_t>(mojom::SensorType::LAST));
+
+    fake_screen_ = std::make_unique<FakeScreen>();
+
+    display::Screen::SetScreenInstance(fake_screen_.get());
+
+    scoped_task_environment_.RunUntilIdle();
+  }
+
+  void TearDown() override { shared_buffer_handle_.reset(); }
+
+  double GetBufferOffset() {
+    return SensorReadingSharedBuffer::GetOffset(
+        mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION);
+  }
+
+  void InitializeDevice(mojom::SensorInitParamsPtr params) {
+    base::RunLoop loop;
+
+    device_ = std::make_unique<VROrientationDevice>(
+        &sensor_provider_ptr_, base::BindOnce(
+                                   [](base::OnceClosure quit_closure) {
+                                     // The callback was called.
+                                     std::move(quit_closure).Run();
+                                   },
+                                   loop.QuitClosure()));
+
+    // Complete the creation of device_ by letting the GetSensor function go
+    // through.
+    scoped_task_environment_.RunUntilIdle();
+
+    fake_sensor_provider_->CallCallback(std::move(params));
+    scoped_task_environment_.RunUntilIdle();
+
+    // Ensure that the callback is called.
+    loop.Run();
+  }
+
+  void DeviceReadPose(gfx::Quaternion input_q,
+                      base::OnceCallback<void(mojom::VRPosePtr)> callback) {
+    // If the device isn't available we can't read a quaternion from it
+    ASSERT_TRUE(device_->IsAvailable());
+
+    WriteToBuffer(input_q);
+
+    base::RunLoop loop;
+
+    device_->OnMagicWindowPoseRequest(base::BindOnce(
+        [](base::OnceClosure quit_closure,
+           base::OnceCallback<void(mojom::VRPosePtr)> callback,
+           mojom::VRPosePtr ptr) {
+          std::move(callback).Run(std::move(ptr));
+          std::move(quit_closure).Run();
+        },
+        loop.QuitClosure(), std::move(callback)));
+
+    scoped_task_environment_.RunUntilIdle();
+
+    // Ensure the pose request callback runs.
+    loop.Run();
+  }
+
+  mojom::SensorInitParamsPtr FakeInitParams() {
+    auto init_params = mojom::SensorInitParams::New();
+    init_params->sensor = std::move(sensor_ptr_);
+    init_params->default_configuration = PlatformSensorConfiguration(
+        SensorTraits<mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION>::
+            kDefaultFrequency);
+
+    init_params->client_request = mojo::MakeRequest(&sensor_client_ptr_);
+
+    init_params->memory = shared_buffer_handle_->Clone(
+        mojo::SharedBufferHandle::AccessMode::READ_ONLY);
+
+    init_params->buffer_offset = GetBufferOffset();
+
+    return init_params;
+  }
+
+  void WriteToBuffer(gfx::Quaternion q) {
+    mojo::ScopedSharedBufferMapping shared_buffer =
+        shared_buffer_handle_->MapAtOffset(
+            mojom::SensorInitParams::kReadBufferSizeForTests,
+            GetBufferOffset());
+
+    SensorReadingSharedBuffer* buffer =
+        static_cast<SensorReadingSharedBuffer*>(shared_buffer.get());
+
+    auto& seqlock = buffer->seqlock.value();
+    seqlock.WriteBegin();
+    buffer->reading.orientation_quat.x = q.x();
+    buffer->reading.orientation_quat.y = q.y();
+    buffer->reading.orientation_quat.z = q.z();
+    buffer->reading.orientation_quat.w = q.w();
+    seqlock.WriteEnd();
+  }
+
+  void SetRotation(display::Display::Rotation rotation) {
+    fake_screen_->display.set_rotation(rotation);
+  }
+
+  // Needed for MakeRequest to work.
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  std::unique_ptr<VROrientationDevice> device_;
+  std::unique_ptr<FakeSensorProvider> fake_sensor_provider_;
+  mojom::SensorProviderPtr sensor_provider_ptr_;
+
+  // Fake Sensor Init params objects
+  std::unique_ptr<FakeOrientationSensor> fake_sensor_;
+  mojom::SensorPtrInfo sensor_ptr_;
+  mojo::ScopedSharedBufferHandle shared_buffer_handle_;
+  mojom::SensorClientPtr sensor_client_ptr_;
+
+  std::unique_ptr<FakeScreen> fake_screen_;
+
+  DISALLOW_COPY_AND_ASSIGN(VROrientationDeviceTest);
+};
+
+TEST_F(VROrientationDeviceTest, InitializationTest) {
+  // Check that without running anything, the device will return not available,
+  // without crashing.
+
+  device_ = std::make_unique<VROrientationDevice>(&sensor_provider_ptr_,
+                                                  base::BindOnce([]() {}));
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(device_->IsAvailable());
+}
+
+TEST_F(VROrientationDeviceTest, SensorNotAvailableTest) {
+  // If the provider calls back with nullptr, there are no sensors available.
+
+  InitializeDevice(nullptr);
+
+  EXPECT_FALSE(device_->IsAvailable());
+}
+
+TEST_F(VROrientationDeviceTest, SensorIsAvailableTest) {
+  // Tests that with proper params the device initializes without mishap.
+
+  InitializeDevice(FakeInitParams());
+
+  EXPECT_TRUE(device_->IsAvailable());
+}
+
+TEST_F(VROrientationDeviceTest, GetOrientationTest) {
+  // Tests that OnMagicWindowPoseRequest returns a pose ptr without mishap.
+
+  InitializeDevice(FakeInitParams());
+
+  DeviceReadPose(
+      gfx::Quaternion(0, 0, 0, 1),
+      base::BindOnce([](mojom::VRPosePtr ptr) { EXPECT_TRUE(ptr); }));
+}
+
+TEST_F(VROrientationDeviceTest, OrientationDefaultForwardTest) {
+  InitializeDevice(FakeInitParams());
+
+  // Set forward to 0 degrees
+  DeviceReadPose(gfx::Quaternion(0, 0, 0, 1),
+                 base::BindOnce([](mojom::VRPosePtr ptr) {
+                   EXPECT_NEAR(ptr->orientation->at(0), -0.707, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(3), 0.707, 0.001);
+                 }));
+
+  // Now a 90 degree rotation around x in device space should be default pose in
+  // vr space.
+  DeviceReadPose(gfx::Quaternion(0.707, 0, 0, 0.707),
+                 base::BindOnce([](mojom::VRPosePtr ptr) {
+                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(3), 1, 0.001);
+                 }));
+}
+
+TEST_F(VROrientationDeviceTest, OrientationSetForwardTest) {
+  InitializeDevice(FakeInitParams());
+
+  // Hold device upright and rotation 45 degrees to left in device space for
+  // setting the forward. With the device upright, this causes the first reading
+  // to be the default pose.
+  DeviceReadPose(gfx::Quaternion(0.653, 0.271, 0.271, 0.653),
+                 base::BindOnce([](mojom::VRPosePtr ptr) {
+                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(3), 1, 0.001);
+                 }));
+
+  // Now hold upright and straigt produces a 45 degree rotation to the right
+  DeviceReadPose(gfx::Quaternion(0.707, 0, 0, 0.707),
+                 base::BindOnce([](mojom::VRPosePtr ptr) {
+                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(1), -0.383, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(3), 0.924, 0.001);
+                 }));
+}
+
+TEST_F(VROrientationDeviceTest, OrientationLandscape90Test) {
+  InitializeDevice(FakeInitParams());
+
+  SetRotation(display::Display::ROTATE_90);
+
+  // Tilting the device up and twisting to the side should be default in
+  // landscape mode.
+  DeviceReadPose(gfx::Quaternion(0.5, -0.5, 0.5, 0.5),
+                 base::BindOnce([](mojom::VRPosePtr ptr) {
+                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(3), 1, 0.001);
+                 }));
+
+  // Rotating the device 45 left from base pose should cause 45 degree left
+  // rotation around y in VR space.
+  DeviceReadPose(gfx::Quaternion(0.653, -0.271, 0.653, 0.271),
+                 base::BindOnce([](mojom::VRPosePtr ptr) {
+                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(1), 0.382, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(3), 0.924, 0.001);
+                 }));
+}
+
+TEST_F(VROrientationDeviceTest, OrientationLandscape270Test) {
+  SetRotation(display::Display::ROTATE_270);
+
+  InitializeDevice(FakeInitParams());
+
+  // Tilting the device up and twisting to the side should be default in
+  // landscape mode (twist the other way from what we'd need for ROTATE_90).
+  DeviceReadPose(gfx::Quaternion(0.5, 0.5, -0.5, 0.5),
+                 base::BindOnce([](mojom::VRPosePtr ptr) {
+                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(1), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(3), 1, 0.001);
+                 }));
+
+  // Rotating the device 45 left from base pose should cause 45 degree left
+  // rotation around y in VR space
+  DeviceReadPose(gfx::Quaternion(0.271, 0.653, -0.271, 0.653),
+                 base::BindOnce([](mojom::VRPosePtr ptr) {
+                   EXPECT_NEAR(ptr->orientation->at(0), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(1), 0.382, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(2), 0, 0.001);
+                   EXPECT_NEAR(ptr->orientation->at(3), 0.924, 0.001);
+                 }));
+}
+
+}  // namespace device
diff --git a/device/vr/test/fake_orientation_provider.cc b/device/vr/test/fake_orientation_provider.cc
new file mode 100644
index 0000000..7c93a9eb
--- /dev/null
+++ b/device/vr/test/fake_orientation_provider.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/vr/test/fake_orientation_provider.h"
+
+#include "services/device/public/interfaces/sensor.mojom.h"
+
+namespace device {
+
+FakeOrientationSensor::FakeOrientationSensor(mojom::SensorRequest request)
+    : binding_(this) {
+  binding_.Bind(std::move(request));
+}
+
+FakeOrientationSensor::~FakeOrientationSensor() = default;
+
+// The called functions
+void FakeOrientationSensor::AddConfiguration(
+    const PlatformSensorConfiguration& configuration,
+    AddConfigurationCallback callback) {
+  std::move(callback).Run(true);
+}
+
+}  // namespace device
\ No newline at end of file
diff --git a/device/vr/test/fake_orientation_provider.h b/device/vr/test/fake_orientation_provider.h
new file mode 100644
index 0000000..f0341cb5
--- /dev/null
+++ b/device/vr/test/fake_orientation_provider.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_VR_TEST_FAKE_ORIENTATION_PROVIDER_H_
+#define DEVICE_VR_TEST_FAKE_ORIENTATION_PROVIDER_H_
+
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/device/public/interfaces/sensor.mojom.h"
+
+namespace device {
+
+class FakeOrientationSensor : public mojom::Sensor {
+ public:
+  FakeOrientationSensor(mojom::SensorRequest request);
+  ~FakeOrientationSensor() override;
+
+  void AddConfiguration(const PlatformSensorConfiguration& configuration,
+                        AddConfigurationCallback callback) override;
+  void ConfigureReadingChangeNotifications(bool enabled) override {}
+
+  void GetDefaultConfiguration(
+      GetDefaultConfigurationCallback callback) override {}
+  void RemoveConfiguration(
+      const PlatformSensorConfiguration& configuration) override {}
+  void Suspend() override {}
+  void Resume() override {}
+
+ private:
+  mojo::Binding<mojom::Sensor> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeOrientationSensor);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_VR_TEST_FAKE_ORIENTATION_PROVIDER_H_
\ No newline at end of file
diff --git a/device/vr/test/fake_sensor_provider.cc b/device/vr/test/fake_sensor_provider.cc
new file mode 100644
index 0000000..d9b82f92
--- /dev/null
+++ b/device/vr/test/fake_sensor_provider.cc
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/vr/test/fake_sensor_provider.h"
+
+#include "services/device/public/interfaces/sensor.mojom.h"
+#include "services/device/public/interfaces/sensor_provider.mojom.h"
+
+namespace device {
+
+FakeSensorProvider::FakeSensorProvider() : binding_(this) {}
+
+FakeSensorProvider::FakeSensorProvider(mojom::SensorProviderRequest request)
+    : binding_(this) {
+  binding_.Bind(std::move(request));
+}
+
+FakeSensorProvider::~FakeSensorProvider() {
+  if (callback_)
+    std::move(callback_).Run(nullptr);
+}
+
+void FakeSensorProvider::Bind(mojo::ScopedMessagePipeHandle handle) {
+  binding_.Bind(mojom::SensorProviderRequest(std::move(handle)));
+}
+
+void FakeSensorProvider::GetSensor(mojom::SensorType type,
+                                   GetSensorCallback callback) {
+  callback_ = std::move(callback);
+}
+
+void FakeSensorProvider::CallCallback(mojom::SensorInitParamsPtr param) {
+  std::move(callback_).Run(std::move(param));
+}
+
+}  // namespace device
\ No newline at end of file
diff --git a/device/vr/test/fake_sensor_provider.h b/device/vr/test/fake_sensor_provider.h
new file mode 100644
index 0000000..583f15e
--- /dev/null
+++ b/device/vr/test/fake_sensor_provider.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_VR_TEST_FAKE_SENSOR_PROVIDER_H_
+#define DEVICE_VR_TEST_FAKE_SENSOR_PROVIDER_H_
+
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/device/public/interfaces/sensor.mojom.h"
+#include "services/device/public/interfaces/sensor_provider.mojom.h"
+
+namespace device {
+
+class FakeSensorProvider : public mojom::SensorProvider {
+ public:
+  FakeSensorProvider();
+  explicit FakeSensorProvider(mojom::SensorProviderRequest request);
+  ~FakeSensorProvider() override;
+
+  void Bind(mojo::ScopedMessagePipeHandle handle);
+  void GetSensor(mojom::SensorType type, GetSensorCallback callback) override;
+  void CallCallback(mojom::SensorInitParamsPtr param);
+
+ private:
+  mojo::Binding<mojom::SensorProvider> binding_;
+  GetSensorCallback callback_;
+};
+
+}  // namespace device
+
+#endif  // DEVICE_VR_TEST_FAKE_SENSOR_PROVIDER_H_
\ No newline at end of file
diff --git a/device/vr/vr_device.h b/device/vr/vr_device.h
index 96163b9..2f8e732 100644
--- a/device/vr/vr_device.h
+++ b/device/vr/vr_device.h
@@ -42,6 +42,9 @@
   virtual mojom::VRDisplayInfoPtr GetVRDisplayInfo() = 0;
   virtual void SetMagicWindowEnabled(bool enabled) = 0;
 
+  // The fallback device should only be provided in lieu of other devices.
+  virtual bool IsFallbackDevice() = 0;
+
   // TODO(mthiesse): The browser should handle browser-side exiting of
   // presentation before device/ is even aware presentation is being exited.
   // Then the browser should call ExitPresent() on Device, which does device/
diff --git a/device/vr/vr_device_base.cc b/device/vr/vr_device_base.cc
index 30dcd2cd..993904f 100644
--- a/device/vr/vr_device_base.cc
+++ b/device/vr/vr_device_base.cc
@@ -46,6 +46,10 @@
   SetPresentingDisplay(nullptr);
 }
 
+bool VRDeviceBase::IsFallbackDevice() {
+  return false;
+};
+
 mojom::VRDisplayInfoPtr VRDeviceBase::GetVRDisplayInfo() {
   DCHECK(display_info_);
   return display_info_.Clone();
diff --git a/device/vr/vr_device_base.h b/device/vr/vr_device_base.h
index d3fb897..a4d0abe 100644
--- a/device/vr/vr_device_base.h
+++ b/device/vr/vr_device_base.h
@@ -39,6 +39,7 @@
       mojom::VRPresentationProviderRequest request,
       mojom::VRDisplayHost::RequestPresentCallback callback);
   virtual void ExitPresent();
+  bool IsFallbackDevice() override;
 
   void AddDisplay(VRDisplayImpl* display);
   void RemoveDisplay(VRDisplayImpl* display);
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc
index dccf5601..2ad713f 100644
--- a/extensions/browser/api/runtime/runtime_api.cc
+++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -235,8 +235,8 @@
     content::BrowserContext* browser_context,
     const Extension* extension,
     UninstallReason reason) {
-  RuntimeEventRouter::OnExtensionUninstalled(
-      browser_context_, extension->id(), reason);
+  RuntimeEventRouter::OnExtensionUninstalled(browser_context_, extension->id(),
+                                             reason);
 }
 
 void RuntimeAPI::Shutdown() {
@@ -572,6 +572,12 @@
     return;
   }
 
+  // Blacklisted extensions should not open uninstall_url.
+  if (extensions::ExtensionPrefs::Get(context)->IsExtensionBlacklisted(
+          extension_id)) {
+    return;
+  }
+
   RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url);
 }
 
diff --git a/extensions/browser/api/runtime/runtime_apitest.cc b/extensions/browser/api/runtime/runtime_apitest.cc
index e6cbc03..2999bdd 100644
--- a/extensions/browser/api/runtime/runtime_apitest.cc
+++ b/extensions/browser/api/runtime/runtime_apitest.cc
@@ -6,15 +6,19 @@
 #include "chrome/browser/apps/app_browsertest_util.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_dir.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/api/runtime/runtime_api.h"
+#include "extensions/browser/blacklist_state.h"
 #include "extensions/browser/extension_dialog_auto_confirm.h"
+#include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/test/result_catcher.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "url/url_constants.h"
 
 // Tests the privileged components of chrome.runtime.
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeRuntimePrivileged) {
@@ -48,6 +52,15 @@
 
 namespace {
 
+const char kUninstallUrl[] = "http://www.google.com/";
+
+std::string GetActiveUrl(Browser* browser) {
+  return browser->tab_strip_model()
+      ->GetActiveWebContents()
+      ->GetLastCommittedURL()
+      .spec();
+}
+
 class RuntimeAPIUpdateTest : public ExtensionApiTest {
  public:
   RuntimeAPIUpdateTest() {}
@@ -214,4 +227,56 @@
   }
 }
 
+// Tests that when a blacklisted extension with a set uninstall url is
+// uninstalled, its uninstall url does not open.
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest,
+                       DoNotOpenUninstallUrlForBlacklistedExtensions) {
+  // Load an extension that has set an uninstall url.
+  scoped_refptr<const extensions::Extension> extension =
+      LoadExtension(test_data_dir_.AppendASCII("runtime")
+                        .AppendASCII("uninstall_url")
+                        .AppendASCII("sets_uninstall_url"));
+
+  ASSERT_TRUE(extension.get());
+  extension_service()->AddExtension(extension.get());
+  ASSERT_TRUE(extension_service()->IsExtensionEnabled(extension->id()));
+
+  // Uninstall the extension and expect its uninstall url to open.
+  extension_service()->UninstallExtension(
+      extension->id(), extensions::UNINSTALL_REASON_USER_INITIATED, NULL);
+  TabStripModel* tabs = browser()->tab_strip_model();
+
+  EXPECT_EQ(2, tabs->count());
+  content::WaitForLoadStop(tabs->GetActiveWebContents());
+  // Verify the uninstall url
+  EXPECT_EQ(kUninstallUrl, GetActiveUrl(browser()));
+
+  // Close the tab pointing to the uninstall url.
+  tabs->CloseWebContentsAt(tabs->active_index(), 0);
+  EXPECT_EQ(1, tabs->count());
+  EXPECT_EQ("about:blank", GetActiveUrl(browser()));
+
+  // Load the same extension again, except blacklist it after installation.
+  extension = LoadExtension(test_data_dir_.AppendASCII("runtime")
+                                .AppendASCII("uninstall_url")
+                                .AppendASCII("sets_uninstall_url"));
+  extension_service()->AddExtension(extension.get());
+  ASSERT_TRUE(extension_service()->IsExtensionEnabled(extension->id()));
+
+  // Blacklist extension.
+  extensions::ExtensionPrefs::Get(profile())->SetExtensionBlacklistState(
+      extension->id(), extensions::BlacklistState::BLACKLISTED_MALWARE);
+
+  // Uninstalling a blacklisted extension should not open its uninstall url.
+  TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()),
+                                         extension->id());
+  extension_service()->UninstallExtension(
+      extension->id(), extensions::UNINSTALL_REASON_USER_INITIATED, NULL);
+  observer.WaitForExtensionUninstalled();
+
+  EXPECT_EQ(1, tabs->count());
+  content::WaitForLoadStop(tabs->GetActiveWebContents());
+  EXPECT_EQ(url::kAboutBlankURL, GetActiveUrl(browser()));
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/guest_view/extension_view/whitelist/OWNERS b/extensions/browser/guest_view/extension_view/whitelist/OWNERS
index 1be42c1..0a3bc083 100644
--- a/extensions/browser/guest_view/extension_view/whitelist/OWNERS
+++ b/extensions/browser/guest_view/extension_view/whitelist/OWNERS
@@ -6,7 +6,6 @@
 brettw@chromium.org
 cpu@chromium.org
 darin@chromium.org
-dglazkov@chromium.org
 jam@chromium.org
 jochen@chromium.org
 
diff --git a/gin/BUILD.gn b/gin/BUILD.gn
index 5341919..39bd188c 100644
--- a/gin/BUILD.gn
+++ b/gin/BUILD.gn
@@ -30,6 +30,15 @@
     "isolate_holder.cc",
     "modules/console.cc",
     "modules/console.h",
+    "modules/file_module_provider.cc",
+    "modules/file_module_provider.h",
+    "modules/module_registry.cc",
+    "modules/module_registry.h",
+    "modules/module_registry_observer.h",
+    "modules/module_runner_delegate.cc",
+    "modules/module_runner_delegate.h",
+    "modules/timer.cc",
+    "modules/timer.h",
     "object_template_builder.cc",
     "object_template_builder.h",
     "per_context_data.cc",
@@ -122,9 +131,20 @@
 source_set("gin_test") {
   testonly = true
   sources = [
+    "test/file.cc",
+    "test/file.h",
+    "test/file_runner.cc",
+    "test/file_runner.h",
+    "test/gc.cc",
+    "test/gc.h",
+    "test/gtest.cc",
+    "test/gtest.h",
     "test/v8_test.cc",
     "test/v8_test.h",
   ]
+  data = [
+    "test/expect.js",
+  ]
 
   public_deps = [
     ":gin",
@@ -149,10 +169,13 @@
     "converter_unittest.cc",
     "data_object_builder_unittest.cc",
     "interceptor_unittest.cc",
+    "modules/module_registry_unittest.cc",
+    "modules/timer_unittest.cc",
     "per_context_data_unittest.cc",
     "shell/gin_shell_unittest.cc",
     "shell_runner_unittest.cc",
     "test/run_all_unittests.cc",
+    "test/run_js_tests.cc",
     "v8_isolate_memory_dump_provider_unittest.cc",
     "v8_platform_unittest.cc",
     "wrappable_unittest.cc",
@@ -168,7 +191,10 @@
   configs += [ "//v8:external_startup_data" ]
 
   data = [
+    "modules/module_registry_unittests.js",
     "shell/hello_world.js",
+    "test/file_unittests.js",
+    "test/gtest_unittests.js",
     "../OWNERS",
   ]
 
diff --git a/gin/modules/console.cc b/gin/modules/console.cc
index 8786392b..63fc41e 100644
--- a/gin/modules/console.cc
+++ b/gin/modules/console.cc
@@ -9,31 +9,41 @@
 #include "base/strings/string_util.h"
 #include "gin/arguments.h"
 #include "gin/converter.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+
+using v8::ObjectTemplate;
 
 namespace gin {
 
 namespace {
 
-void Log(const v8::FunctionCallbackInfo<v8::Value>& info) {
-  Arguments args(info);
+void Log(Arguments* args) {
   std::vector<std::string> messages;
-  if (!args.GetRemaining(&messages)) {
-    args.ThrowError();
+  if (!args->GetRemaining(&messages)) {
+    args->ThrowError();
     return;
   }
   printf("%s\n", base::JoinString(messages, " ").c_str());
 }
 
+WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
+
 }  // namespace
 
-// static
-void Console::Register(v8::Isolate* isolate,
-                       v8::Local<v8::ObjectTemplate> templ) {
-  v8::Local<v8::FunctionTemplate> log_templ =
-      v8::FunctionTemplate::New(isolate, Log);
-  log_templ->RemovePrototype();
+const char Console::kModuleName[] = "console";
 
-  templ->Set(StringToSymbol(isolate, "log"), log_templ);
+v8::Local<v8::Value> Console::GetModule(v8::Isolate* isolate) {
+  PerIsolateData* data = PerIsolateData::From(isolate);
+  v8::Local<ObjectTemplate> templ = data->GetObjectTemplate(&g_wrapper_info);
+  if (templ.IsEmpty()) {
+    templ = ObjectTemplateBuilder(isolate)
+        .SetMethod("log", Log)
+        .Build();
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+  return templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
 }
 
 }  // namespace gin
diff --git a/gin/modules/console.h b/gin/modules/console.h
index 4b68aa1..ff8061ba 100644
--- a/gin/modules/console.h
+++ b/gin/modules/console.h
@@ -14,8 +14,8 @@
 // we'd like to evolve the API to match window.console in browsers.
 class GIN_EXPORT Console {
  public:
-  static void Register(v8::Isolate* isolate,
-                       v8::Local<v8::ObjectTemplate> templ);
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
 };
 
 }  // namespace gin
diff --git a/gin/modules/file_module_provider.cc b/gin/modules/file_module_provider.cc
new file mode 100644
index 0000000..2edb6088
--- /dev/null
+++ b/gin/modules/file_module_provider.cc
@@ -0,0 +1,74 @@
+// Copyright 2013 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 "gin/modules/file_module_provider.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_split.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "gin/converter.h"
+
+namespace gin {
+
+namespace {
+
+void AttempToLoadModule(const base::WeakPtr<Runner>& runner,
+                        const std::vector<base::FilePath>& search_paths,
+                        const std::string& id) {
+  if (!runner)
+    return;
+
+  std::vector<std::string> components = base::SplitString(
+      id, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+  base::FilePath path;
+  for (size_t i = 0; i < components.size(); ++i) {
+    // TODO(abarth): Technically the path components can be UTF-8. We don't
+    // handle that case correctly yet.
+    path = path.AppendASCII(components[i]);
+  }
+  path = path.AddExtension(FILE_PATH_LITERAL("js"));
+
+  for (size_t i = 0; i < search_paths.size(); ++i) {
+    std::string source;
+    if (!ReadFileToString(search_paths[i].Append(path), &source))
+      continue;
+
+    Runner::Scope scope(runner.get());
+    runner->Run(source, id);
+    return;
+  }
+  LOG(ERROR) << "Failed to load module from disk: " << id;
+}
+
+}  // namespace
+
+FileModuleProvider::FileModuleProvider(
+    const std::vector<base::FilePath>& search_paths)
+    : search_paths_(search_paths) {
+}
+
+FileModuleProvider::~FileModuleProvider() = default;
+
+void FileModuleProvider::AttempToLoadModules(
+    Runner* runner, const std::set<std::string>& ids) {
+  std::set<std::string> modules = ids;
+  for (std::set<std::string>::const_iterator it = modules.begin();
+       it != modules.end(); ++it) {
+    const std::string& id = *it;
+    if (attempted_ids_.count(id))
+      continue;
+    attempted_ids_.insert(id);
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(AttempToLoadModule, runner->GetWeakPtr(),
+                              search_paths_, id));
+  }
+}
+
+}  // namespace gin
diff --git a/gin/modules/file_module_provider.h b/gin/modules/file_module_provider.h
new file mode 100644
index 0000000..7c03887
--- /dev/null
+++ b/gin/modules/file_module_provider.h
@@ -0,0 +1,45 @@
+// Copyright 2013 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 GIN_MODULES_FILE_MODULE_PROVIDER_H_
+#define GIN_MODULES_FILE_MODULE_PROVIDER_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "gin/gin_export.h"
+#include "gin/runner.h"
+
+namespace gin {
+
+// FileModuleProvider knows how to load AMD modules off disk. It searches for
+// modules in the directories indiciated by |search_paths|. Although we still
+// read from the file system on the main thread, we'll eventually want to move
+// the reads to a background thread.
+class GIN_EXPORT FileModuleProvider {
+ public:
+  explicit FileModuleProvider(
+      const std::vector<base::FilePath>& search_paths);
+  ~FileModuleProvider();
+
+  // Searches for modules with |ids| in the file system. If found, the modules
+  // will be executed asynchronously by |runner|.
+  void AttempToLoadModules(Runner* runner, const std::set<std::string>& ids);
+
+ private:
+  std::vector<base::FilePath> search_paths_;
+
+  // We'll only search for a given module once. We remember the set of modules
+  // we've already looked for in |attempted_ids_|.
+  std::set<std::string> attempted_ids_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileModuleProvider);
+};
+
+}  // namespace gin
+
+#endif  // GIN_MODULES_FILE_MODULE_PROVIDER_H_
diff --git a/gin/modules/module_registry.cc b/gin/modules/module_registry.cc
new file mode 100644
index 0000000..cf61f7d
--- /dev/null
+++ b/gin/modules/module_registry.cc
@@ -0,0 +1,289 @@
+// Copyright 2013 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 "gin/modules/module_registry.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/modules/module_registry_observer.h"
+#include "gin/per_context_data.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/runner.h"
+
+using v8::Context;
+using v8::External;
+using v8::Function;
+using v8::FunctionTemplate;
+using v8::Isolate;
+using v8::Local;
+using v8::Object;
+using v8::ObjectTemplate;
+using v8::Persistent;
+using v8::StackTrace;
+using v8::String;
+using v8::Value;
+
+namespace gin {
+
+struct PendingModule {
+  PendingModule();
+  ~PendingModule();
+
+  std::string id;
+  std::vector<std::string> dependencies;
+  Persistent<Value> factory;
+};
+
+PendingModule::PendingModule() = default;
+
+PendingModule::~PendingModule() {
+  factory.Reset();
+}
+
+namespace {
+
+// Key for base::SupportsUserData::Data.
+const char kModuleRegistryKey[] = "ModuleRegistry";
+
+struct ModuleRegistryData : public base::SupportsUserData::Data {
+  std::unique_ptr<ModuleRegistry> registry;
+};
+
+void Define(const v8::FunctionCallbackInfo<Value>& info) {
+  Arguments args(info);
+
+  if (!info.Length())
+    return args.ThrowTypeError("At least one argument is required.");
+
+  std::string id;
+  std::vector<std::string> dependencies;
+  v8::Local<Value> factory;
+
+  if (!args.PeekNext().IsEmpty() && args.PeekNext()->IsString())
+    args.GetNext(&id);
+  if (!args.PeekNext().IsEmpty() && args.PeekNext()->IsArray())
+    args.GetNext(&dependencies);
+  if (!args.GetNext(&factory))
+    return args.ThrowError();
+
+  std::unique_ptr<PendingModule> pending(new PendingModule);
+  pending->id = id;
+  pending->dependencies = dependencies;
+  pending->factory.Reset(args.isolate(), factory);
+
+  ModuleRegistry* registry =
+      ModuleRegistry::From(args.isolate()->GetCurrentContext());
+  registry->AddPendingModule(args.isolate(), std::move(pending));
+}
+
+WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
+
+Local<FunctionTemplate> GetDefineTemplate(Isolate* isolate) {
+  PerIsolateData* data = PerIsolateData::From(isolate);
+  Local<FunctionTemplate> templ = data->GetFunctionTemplate(
+      &g_wrapper_info);
+  if (templ.IsEmpty()) {
+    templ = FunctionTemplate::New(isolate, Define);
+    templ->RemovePrototype();
+    data->SetFunctionTemplate(&g_wrapper_info, templ);
+  }
+  return templ;
+}
+
+}  // namespace
+
+ModuleRegistry::ModuleRegistry(Isolate* isolate)
+    : modules_(isolate, Object::New(isolate)) {
+}
+
+ModuleRegistry::~ModuleRegistry() {
+  modules_.Reset();
+}
+
+// static
+void ModuleRegistry::RegisterGlobals(Isolate* isolate,
+                                     v8::Local<ObjectTemplate> templ) {
+  templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate));
+}
+
+// static
+bool ModuleRegistry::InstallGlobals(v8::Isolate* isolate,
+                                    v8::Local<v8::Object> obj) {
+  v8::Local<v8::Function> function;
+  auto maybe_function =
+      GetDefineTemplate(isolate)->GetFunction(isolate->GetCurrentContext());
+  if (!maybe_function.ToLocal(&function))
+    return false;
+  return SetProperty(isolate, obj, StringToSymbol(isolate, "define"), function);
+}
+
+// static
+ModuleRegistry* ModuleRegistry::From(v8::Local<Context> context) {
+  PerContextData* data = PerContextData::From(context);
+  if (!data)
+    return NULL;
+
+  ModuleRegistryData* registry_data = static_cast<ModuleRegistryData*>(
+      data->GetUserData(kModuleRegistryKey));
+  if (!registry_data) {
+    // PerContextData takes ownership of ModuleRegistryData.
+    registry_data = new ModuleRegistryData;
+    registry_data->registry.reset(new ModuleRegistry(context->GetIsolate()));
+    data->SetUserData(kModuleRegistryKey, base::WrapUnique(registry_data));
+  }
+  return registry_data->registry.get();
+}
+
+void ModuleRegistry::AddObserver(ModuleRegistryObserver* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void ModuleRegistry::RemoveObserver(ModuleRegistryObserver* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id,
+                                      v8::Local<Value> module) {
+  DCHECK(!id.empty());
+  RegisterModule(isolate, id, module);
+}
+
+void ModuleRegistry::AddPendingModule(Isolate* isolate,
+                                      std::unique_ptr<PendingModule> pending) {
+  const std::string pending_id = pending->id;
+  const std::vector<std::string> pending_dependencies = pending->dependencies;
+  AttemptToLoad(isolate, std::move(pending));
+  for (auto& observer : observer_list_)
+    observer.OnDidAddPendingModule(pending_id, pending_dependencies);
+}
+
+void ModuleRegistry::LoadModule(Isolate* isolate,
+                                const std::string& id,
+                                LoadModuleCallback callback) {
+  if (available_modules_.find(id) != available_modules_.end()) {
+    // Should we call the callback asynchronously?
+    callback.Run(GetModule(isolate, id));
+    return;
+  }
+  waiting_callbacks_.insert(std::make_pair(id, callback));
+
+  for (size_t i = 0; i < pending_modules_.size(); ++i) {
+    if (pending_modules_[i]->id == id)
+      return;
+  }
+
+  unsatisfied_dependencies_.insert(id);
+}
+
+bool ModuleRegistry::RegisterModule(Isolate* isolate,
+                                    const std::string& id,
+                                    v8::Local<Value> module) {
+  if (id.empty() || module.IsEmpty())
+    return false;
+
+  v8::Local<Object> modules = Local<Object>::New(isolate, modules_);
+  if (!SetProperty(isolate, modules, StringToSymbol(isolate, id), module))
+    return false;
+  unsatisfied_dependencies_.erase(id);
+  available_modules_.insert(id);
+
+  std::pair<LoadModuleCallbackMap::iterator, LoadModuleCallbackMap::iterator>
+      range = waiting_callbacks_.equal_range(id);
+  std::vector<LoadModuleCallback> callbacks;
+  callbacks.reserve(waiting_callbacks_.count(id));
+  for (LoadModuleCallbackMap::iterator it = range.first; it != range.second;
+       ++it) {
+    callbacks.push_back(it->second);
+  }
+  waiting_callbacks_.erase(range.first, range.second);
+  for (std::vector<LoadModuleCallback>::iterator it = callbacks.begin();
+       it != callbacks.end();
+       ++it) {
+    // Should we call the callback asynchronously?
+    it->Run(module);
+  }
+  return true;
+}
+
+bool ModuleRegistry::CheckDependencies(PendingModule* pending) {
+  size_t num_missing_dependencies = 0;
+  size_t len = pending->dependencies.size();
+  for (size_t i = 0; i < len; ++i) {
+    const std::string& dependency = pending->dependencies[i];
+    if (available_modules_.count(dependency))
+      continue;
+    unsatisfied_dependencies_.insert(dependency);
+    num_missing_dependencies++;
+  }
+  return num_missing_dependencies == 0;
+}
+
+bool ModuleRegistry::Load(Isolate* isolate,
+                          std::unique_ptr<PendingModule> pending) {
+  if (!pending->id.empty() && available_modules_.count(pending->id))
+    return true;  // We've already loaded this module.
+
+  uint32_t argc = static_cast<uint32_t>(pending->dependencies.size());
+  std::vector<v8::Local<Value> > argv(argc);
+  for (uint32_t i = 0; i < argc; ++i)
+    argv[i] = GetModule(isolate, pending->dependencies[i]);
+
+  v8::Local<Value> module = Local<Value>::New(isolate, pending->factory);
+
+  v8::Local<Function> factory;
+  if (ConvertFromV8(isolate, module, &factory)) {
+    PerContextData* data = PerContextData::From(isolate->GetCurrentContext());
+    Runner* runner = data->runner();
+    module = runner->Call(factory, runner->global(), argc,
+                          argv.empty() ? NULL : &argv.front());
+    if (pending->id.empty())
+      ConvertFromV8(isolate, factory->GetScriptOrigin().ResourceName(),
+                    &pending->id);
+  }
+
+  return RegisterModule(isolate, pending->id, module);
+}
+
+bool ModuleRegistry::AttemptToLoad(Isolate* isolate,
+                                   std::unique_ptr<PendingModule> pending) {
+  if (!CheckDependencies(pending.get())) {
+    pending_modules_.push_back(std::move(pending));
+    return false;
+  }
+  return Load(isolate, std::move(pending));
+}
+
+v8::Local<v8::Value> ModuleRegistry::GetModule(v8::Isolate* isolate,
+                                                const std::string& id) {
+  v8::Local<Object> modules = Local<Object>::New(isolate, modules_);
+  v8::Local<String> key = StringToSymbol(isolate, id);
+  DCHECK(modules->HasOwnProperty(isolate->GetCurrentContext(), key).FromJust());
+  return modules->Get(isolate->GetCurrentContext(), key).ToLocalChecked();
+}
+
+void ModuleRegistry::AttemptToLoadMoreModules(Isolate* isolate) {
+  bool keep_trying = true;
+  while (keep_trying) {
+    keep_trying = false;
+    PendingModuleVector pending_modules;
+    pending_modules.swap(pending_modules_);
+    for (size_t i = 0; i < pending_modules.size(); ++i) {
+      std::unique_ptr<PendingModule> pending(std::move(pending_modules[i]));
+      pending_modules[i] = NULL;
+      if (AttemptToLoad(isolate, std::move(pending)))
+        keep_trying = true;
+    }
+  }
+}
+
+}  // namespace gin
diff --git a/gin/modules/module_registry.h b/gin/modules/module_registry.h
new file mode 100644
index 0000000..c1d3a00
--- /dev/null
+++ b/gin/modules/module_registry.h
@@ -0,0 +1,111 @@
+// Copyright 2013 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 GIN_MODULES_MODULE_REGISTRY_H_
+#define GIN_MODULES_MODULE_REGISTRY_H_
+
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "gin/gin_export.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class ModuleRegistryObserver;
+struct PendingModule;
+
+// This class implements the Asynchronous Module Definition (AMD) API.
+// https://github.com/amdjs/amdjs-api/wiki/AMD
+//
+// Our implementation isn't complete yet. Missing features:
+//   1) Built-in support for require, exports, and module.
+//   2) Path resoltuion in module names.
+//
+// For these reasons, we don't have an "amd" property on the "define"
+// function. The spec says we should only add that property once our
+// implementation complies with the specification.
+//
+class GIN_EXPORT ModuleRegistry {
+ public:
+  typedef base::Callback<void (v8::Local<v8::Value>)> LoadModuleCallback;
+
+  virtual ~ModuleRegistry();
+
+  static ModuleRegistry* From(v8::Local<v8::Context> context);
+
+  static void RegisterGlobals(v8::Isolate* isolate,
+                              v8::Local<v8::ObjectTemplate> templ);
+
+  // Installs the necessary functions needed for modules.
+  // WARNING: this may execute script in the page.
+  static bool InstallGlobals(v8::Isolate* isolate, v8::Local<v8::Object> obj);
+
+  void AddObserver(ModuleRegistryObserver* observer);
+  void RemoveObserver(ModuleRegistryObserver* observer);
+
+  // The caller must have already entered our context.
+  void AddBuiltinModule(v8::Isolate* isolate, const std::string& id,
+                        v8::Local<v8::Value> module);
+
+  // The caller must have already entered our context.
+  void AddPendingModule(v8::Isolate* isolate,
+                        std::unique_ptr<PendingModule> pending);
+
+  void LoadModule(v8::Isolate* isolate,
+                  const std::string& id,
+                  LoadModuleCallback callback);
+
+  // The caller must have already entered our context.
+  void AttemptToLoadMoreModules(v8::Isolate* isolate);
+
+  const std::set<std::string>& available_modules() const {
+    return available_modules_;
+  }
+
+  const std::set<std::string>& unsatisfied_dependencies() const {
+    return unsatisfied_dependencies_;
+  }
+
+ private:
+  typedef std::vector<std::unique_ptr<PendingModule>> PendingModuleVector;
+  typedef std::multimap<std::string, LoadModuleCallback> LoadModuleCallbackMap;
+
+  explicit ModuleRegistry(v8::Isolate* isolate);
+
+  bool Load(v8::Isolate* isolate, std::unique_ptr<PendingModule> pending);
+  bool RegisterModule(v8::Isolate* isolate,
+                      const std::string& id,
+                      v8::Local<v8::Value> module);
+
+  bool CheckDependencies(PendingModule* pending);
+  bool AttemptToLoad(v8::Isolate* isolate,
+                     std::unique_ptr<PendingModule> pending);
+
+  v8::Local<v8::Value> GetModule(v8::Isolate* isolate, const std::string& id);
+
+  std::set<std::string> available_modules_;
+  std::set<std::string> unsatisfied_dependencies_;
+
+  LoadModuleCallbackMap waiting_callbacks_;
+
+  PendingModuleVector pending_modules_;
+  v8::Persistent<v8::Object> modules_;
+
+  base::ObserverList<ModuleRegistryObserver> observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModuleRegistry);
+};
+
+}  // namespace gin
+
+#endif  // GIN_MODULES_MODULE_REGISTRY_H_
diff --git a/gin/modules/module_registry_observer.h b/gin/modules/module_registry_observer.h
new file mode 100644
index 0000000..68ee4ad
--- /dev/null
+++ b/gin/modules/module_registry_observer.h
@@ -0,0 +1,31 @@
+// 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 GIN_MODULES_MODULE_REGISTRY_OBSERVER_H_
+#define GIN_MODULES_MODULE_REGISTRY_OBSERVER_H_
+
+#include <string>
+#include <vector>
+
+#include "gin/gin_export.h"
+
+namespace gin {
+
+// Notified of interesting events from ModuleRegistry.
+class GIN_EXPORT ModuleRegistryObserver {
+ public:
+  // Called from AddPendingModule(). |id| is the id/name of the module and
+  // |dependencies| this list of modules |id| depends upon.
+  virtual void OnDidAddPendingModule(
+      const std::string& id,
+      const std::vector<std::string>& dependencies) = 0;
+
+ protected:
+  virtual ~ModuleRegistryObserver() {}
+};
+
+}  // namespace gin
+
+#endif  // GIN_MODULES_MODULE_REGISTRY_OBSERVER_H_
+
diff --git a/gin/modules/module_registry_unittest.cc b/gin/modules/module_registry_unittest.cc
new file mode 100644
index 0000000..3921539
--- /dev/null
+++ b/gin/modules/module_registry_unittest.cc
@@ -0,0 +1,167 @@
+// 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 "gin/modules/module_registry.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "gin/modules/module_registry_observer.h"
+#include "gin/modules/module_runner_delegate.h"
+#include "gin/public/context_holder.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/shell_runner.h"
+#include "gin/test/v8_test.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+namespace {
+
+struct TestHelper {
+  TestHelper(v8::Isolate* isolate)
+      : delegate(std::vector<base::FilePath>()),
+        runner(new ShellRunner(&delegate, isolate)),
+        scope(runner.get()) {
+  }
+
+  ModuleRunnerDelegate delegate;
+  std::unique_ptr<ShellRunner> runner;
+  Runner::Scope scope;
+};
+
+class ModuleRegistryObserverImpl : public ModuleRegistryObserver {
+ public:
+  ModuleRegistryObserverImpl() : did_add_count_(0) {}
+
+  void OnDidAddPendingModule(
+      const std::string& id,
+      const std::vector<std::string>& dependencies) override {
+    did_add_count_++;
+    id_ = id;
+    dependencies_ = dependencies;
+  }
+
+  int did_add_count() { return did_add_count_; }
+  const std::string& id() const { return id_; }
+  const std::vector<std::string>& dependencies() const { return dependencies_; }
+
+ private:
+  int did_add_count_;
+  std::string id_;
+  std::vector<std::string> dependencies_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModuleRegistryObserverImpl);
+};
+
+void NestedCallback(v8::Local<v8::Value> value) {
+  FAIL() << "Should not be called";
+}
+
+void OnModuleLoaded(TestHelper* helper,
+                    v8::Isolate* isolate,
+                    int64_t* counter,
+                    v8::Local<v8::Value> value) {
+  ASSERT_TRUE(value->IsNumber());
+  v8::Local<v8::Integer> int_value = v8::Local<v8::Integer>::Cast(value);
+  *counter += int_value->Value();
+  ModuleRegistry::From(helper->runner->GetContextHolder()->context())
+      ->LoadModule(isolate, "two", base::Bind(NestedCallback));
+}
+
+void OnModuleLoadedNoOp(v8::Local<v8::Value> value) {
+  ASSERT_TRUE(value->IsNumber());
+}
+
+}  // namespace
+
+typedef V8Test ModuleRegistryTest;
+
+// Verifies ModuleRegistry is not available after ContextHolder has been
+// deleted.
+TEST_F(ModuleRegistryTest, DestroyedWithContext) {
+  v8::Isolate::Scope isolate_scope(instance_->isolate());
+  v8::HandleScope handle_scope(instance_->isolate());
+  v8::Local<v8::Context> context = v8::Context::New(
+      instance_->isolate(), NULL, v8::Local<v8::ObjectTemplate>());
+  {
+    ContextHolder context_holder(instance_->isolate());
+    context_holder.SetContext(context);
+    ModuleRegistry* registry = ModuleRegistry::From(context);
+    EXPECT_TRUE(registry != NULL);
+  }
+  ModuleRegistry* registry = ModuleRegistry::From(context);
+  EXPECT_TRUE(registry == NULL);
+}
+
+// Verifies ModuleRegistryObserver is notified appropriately.
+TEST_F(ModuleRegistryTest, ModuleRegistryObserverTest) {
+  TestHelper helper(instance_->isolate());
+  std::string source =
+     "define('id', ['dep1', 'dep2'], function() {"
+     "  return function() {};"
+     "});";
+
+  ModuleRegistryObserverImpl observer;
+  ModuleRegistry::From(helper.runner->GetContextHolder()->context())->
+      AddObserver(&observer);
+  helper.runner->Run(source, "script");
+  ModuleRegistry::From(helper.runner->GetContextHolder()->context())->
+      RemoveObserver(&observer);
+  EXPECT_EQ(1, observer.did_add_count());
+  EXPECT_EQ("id", observer.id());
+  ASSERT_EQ(2u, observer.dependencies().size());
+  EXPECT_EQ("dep1", observer.dependencies()[0]);
+  EXPECT_EQ("dep2", observer.dependencies()[1]);
+}
+
+// Verifies that multiple LoadModule calls for the same module are handled
+// correctly.
+TEST_F(ModuleRegistryTest, LoadModuleTest) {
+  TestHelper helper(instance_->isolate());
+  int64_t counter = 0;
+  std::string source =
+      "define('one', [], function() {"
+      "  return 1;"
+      "});";
+
+  ModuleRegistry::LoadModuleCallback callback =
+      base::Bind(OnModuleLoaded, &helper, instance_->isolate(), &counter);
+  for (int i = 0; i < 3; i++) {
+    ModuleRegistry::From(helper.runner->GetContextHolder()->context())
+        ->LoadModule(instance_->isolate(), "one", callback);
+  }
+  EXPECT_EQ(0, counter);
+  helper.runner->Run(source, "script");
+  EXPECT_EQ(3, counter);
+}
+
+// Verifies that explicitly loading a module that's already pending does
+// not cause the ModuleRegistry's unsatisfied_dependency set to grow.
+TEST_F(ModuleRegistryTest, UnsatisfiedDependenciesTest) {
+  TestHelper helper(instance_->isolate());
+  std::string source =
+      "define('one', ['no_such_module'], function(nsm) {"
+      "  return 1;"
+      "});";
+  ModuleRegistry* registry =
+    ModuleRegistry::From(helper.runner->GetContextHolder()->context());
+
+  std::set<std::string> no_such_module_set;
+  no_such_module_set.insert("no_such_module");
+
+  // Adds one unsatisfied dependency on "no-such-module".
+  helper.runner->Run(source, "script");
+  EXPECT_EQ(no_such_module_set, registry->unsatisfied_dependencies());
+
+  // Should have no effect on the unsatisfied_dependencies set.
+  ModuleRegistry::LoadModuleCallback callback = base::Bind(OnModuleLoadedNoOp);
+  registry->LoadModule(instance_->isolate(), "one", callback);
+  EXPECT_EQ(no_such_module_set, registry->unsatisfied_dependencies());
+}
+
+}  // namespace gin
diff --git a/gin/modules/module_registry_unittests.js b/gin/modules/module_registry_unittests.js
new file mode 100644
index 0000000..ca70148
--- /dev/null
+++ b/gin/modules/module_registry_unittests.js
@@ -0,0 +1,30 @@
+// Copyright 2013 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.
+
+define("module0", function() {
+  return {
+    "foo": "bar",
+  }
+});
+
+define("module2", [
+    "gtest",
+    "module0",
+    "module1"
+  ], function(gtest, module0, module1) {
+  gtest.expectEqual(module0.foo, "bar",
+      "module0.foo is " + module0.foo);
+  gtest.expectFalse(module0.bar,
+      "module0.bar is " + module0.bar);
+  gtest.expectEqual(module1.baz, "qux",
+      "module1.baz is " + module1.baz);
+  gtest.expectFalse(module1.qux,
+      "module1.qux is " + module1.qux);
+
+  this.result = "PASS";
+});
+
+define("module1", {
+  "baz": "qux",
+});
diff --git a/gin/modules/module_runner_delegate.cc b/gin/modules/module_runner_delegate.cc
new file mode 100644
index 0000000..0634fff3
--- /dev/null
+++ b/gin/modules/module_runner_delegate.cc
@@ -0,0 +1,66 @@
+// Copyright 2013 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 "gin/modules/module_runner_delegate.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "gin/modules/module_registry.h"
+#include "gin/object_template_builder.h"
+#include "gin/public/context_holder.h"
+
+namespace gin {
+
+ModuleRunnerDelegate::ModuleRunnerDelegate(
+  const std::vector<base::FilePath>& search_paths)
+    : module_provider_(search_paths) {
+}
+
+ModuleRunnerDelegate::~ModuleRunnerDelegate() = default;
+
+void ModuleRunnerDelegate::AddBuiltinModule(const std::string& id,
+                                            ModuleGetter getter) {
+  builtin_modules_[id] = base::Bind(getter);
+}
+
+void ModuleRunnerDelegate::AddBuiltinModule(const std::string& id,
+    const ModuleGetterCallback& getter) {
+  builtin_modules_[id] = getter;
+}
+
+void ModuleRunnerDelegate::AttemptToLoadMoreModules(Runner* runner) {
+  ModuleRegistry* registry = ModuleRegistry::From(
+      runner->GetContextHolder()->context());
+  registry->AttemptToLoadMoreModules(runner->GetContextHolder()->isolate());
+  module_provider_.AttempToLoadModules(
+      runner, registry->unsatisfied_dependencies());
+}
+
+v8::Local<v8::ObjectTemplate> ModuleRunnerDelegate::GetGlobalTemplate(
+    ShellRunner* runner,
+    v8::Isolate* isolate) {
+  v8::Local<v8::ObjectTemplate> templ = ObjectTemplateBuilder(isolate).Build();
+  ModuleRegistry::RegisterGlobals(isolate, templ);
+  return templ;
+}
+
+void ModuleRunnerDelegate::DidCreateContext(ShellRunner* runner) {
+  ShellRunnerDelegate::DidCreateContext(runner);
+
+  v8::Local<v8::Context> context = runner->GetContextHolder()->context();
+  ModuleRegistry* registry = ModuleRegistry::From(context);
+
+  v8::Isolate* isolate = runner->GetContextHolder()->isolate();
+
+  for (BuiltinModuleMap::const_iterator it = builtin_modules_.begin();
+       it != builtin_modules_.end(); ++it) {
+    registry->AddBuiltinModule(isolate, it->first, it->second.Run(isolate));
+  }
+}
+
+void ModuleRunnerDelegate::DidRunScript(ShellRunner* runner) {
+  AttemptToLoadMoreModules(runner);
+}
+
+}  // namespace gin
diff --git a/gin/modules/module_runner_delegate.h b/gin/modules/module_runner_delegate.h
new file mode 100644
index 0000000..f49594c
--- /dev/null
+++ b/gin/modules/module_runner_delegate.h
@@ -0,0 +1,57 @@
+// Copyright 2013 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 GIN_MODULES_MODULE_RUNNER_DELEGATE_H_
+#define GIN_MODULES_MODULE_RUNNER_DELEGATE_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "gin/gin_export.h"
+#include "gin/modules/file_module_provider.h"
+#include "gin/shell_runner.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+typedef v8::Local<v8::Value> (*ModuleGetter)(v8::Isolate* isolate);
+typedef base::Callback<v8::Local<v8::Value>(v8::Isolate*)> ModuleGetterCallback;
+
+// Emebedders that use AMD modules will probably want to use a RunnerDelegate
+// that inherits from ModuleRunnerDelegate. ModuleRunnerDelegate lets embedders
+// register built-in modules and routes module requests to FileModuleProvider.
+class GIN_EXPORT ModuleRunnerDelegate : public ShellRunnerDelegate {
+ public:
+  explicit ModuleRunnerDelegate(
+      const std::vector<base::FilePath>& search_paths);
+  ~ModuleRunnerDelegate() override;
+
+  void AddBuiltinModule(const std::string& id, ModuleGetter getter);
+  void AddBuiltinModule(const std::string& id,
+                        const ModuleGetterCallback& getter);
+
+ protected:
+  void AttemptToLoadMoreModules(Runner* runner);
+
+ private:
+  typedef std::map<std::string, ModuleGetterCallback> BuiltinModuleMap;
+
+  // From ShellRunnerDelegate:
+  v8::Local<v8::ObjectTemplate> GetGlobalTemplate(
+      ShellRunner* runner,
+      v8::Isolate* isolate) override;
+  void DidCreateContext(ShellRunner* runner) override;
+  void DidRunScript(ShellRunner* runner) override;
+
+  BuiltinModuleMap builtin_modules_;
+  FileModuleProvider module_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModuleRunnerDelegate);
+};
+
+}  // namespace gin
+
+#endif  // GIN_MODULES_MODULE_RUNNER_DELEGATE_H_
diff --git a/gin/modules/timer.cc b/gin/modules/timer.cc
new file mode 100644
index 0000000..1f60900
--- /dev/null
+++ b/gin/modules/timer.cc
@@ -0,0 +1,112 @@
+// Copyright 2013 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 "gin/modules/timer.h"
+
+#include "base/bind.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_context_data.h"
+
+namespace gin {
+
+namespace {
+
+v8::Local<v8::Private> GetHiddenPropertyName(v8::Isolate* isolate) {
+  return v8::Private::ForApi(isolate, gin::StringToV8(isolate, "::gin::Timer"));
+}
+
+}  // namespace
+
+// Timer =======================================================================
+
+gin::WrapperInfo Timer::kWrapperInfo = { gin::kEmbedderNativeGin };
+
+// static
+Handle<Timer> Timer::Create(TimerType type, v8::Isolate* isolate, int delay_ms,
+                            v8::Local<v8::Function> function) {
+  return CreateHandle(isolate, new Timer(isolate, type == TYPE_REPEATING,
+                                         delay_ms, function));
+}
+
+ObjectTemplateBuilder Timer::GetObjectTemplateBuilder(v8::Isolate* isolate) {
+  // We use Unretained() here because we directly own timer_, so we know it will
+  // be alive when these methods are called.
+  return Wrappable<Timer>::GetObjectTemplateBuilder(isolate)
+      .SetMethod("cancel",
+                 base::Bind(&base::Timer::Stop, base::Unretained(&timer_)))
+      .SetMethod("reset",
+                 base::Bind(&base::Timer::Reset, base::Unretained(&timer_)));
+}
+
+Timer::Timer(v8::Isolate* isolate, bool repeating, int delay_ms,
+             v8::Local<v8::Function> function)
+    : timer_(false, repeating),
+      runner_(PerContextData::From(
+          isolate->GetCurrentContext())->runner()->GetWeakPtr()),
+      weak_factory_(this) {
+  GetWrapper(runner_->GetContextHolder()->isolate())
+      .ToLocalChecked()
+      ->SetPrivate(isolate->GetCurrentContext(), GetHiddenPropertyName(isolate),
+                   function)
+      .FromJust();
+  timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms),
+               base::Bind(&Timer::OnTimerFired, weak_factory_.GetWeakPtr()));
+}
+
+Timer::~Timer() = default;
+
+void Timer::OnTimerFired() {
+  // This can happen in spite of the weak callback because it is possible for
+  // a gin::Handle<> to keep this object alive past when the isolate it is part
+  // of is destroyed.
+  if (!runner_.get()) {
+    return;
+  }
+
+  Runner::Scope scope(runner_.get());
+  v8::Isolate* isolate = runner_->GetContextHolder()->isolate();
+
+  v8::Local<v8::Object> wrapper;
+  if (!GetWrapper(isolate).ToLocal(&wrapper)) {
+    return;
+  }
+
+  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
+      wrapper
+          ->GetPrivate(runner_->GetContextHolder()->context(),
+                       GetHiddenPropertyName(isolate))
+          .ToLocalChecked());
+  runner_->Call(function, v8::Undefined(isolate), 0, NULL);
+}
+
+
+// TimerModule =================================================================
+
+const char TimerModule::kName[] = "timer";
+WrapperInfo TimerModule::kWrapperInfo = { kEmbedderNativeGin };
+
+// static
+Handle<TimerModule> TimerModule::Create(v8::Isolate* isolate) {
+  return CreateHandle(isolate, new TimerModule());
+}
+
+// static
+v8::Local<v8::Value> TimerModule::GetModule(v8::Isolate* isolate) {
+  return Create(isolate)->GetWrapper(isolate).ToLocalChecked();
+}
+
+TimerModule::TimerModule() = default;
+
+TimerModule::~TimerModule() = default;
+
+ObjectTemplateBuilder TimerModule::GetObjectTemplateBuilder(
+    v8::Isolate* isolate) {
+  return Wrappable<TimerModule>::GetObjectTemplateBuilder(isolate)
+      .SetMethod("createOneShot",
+                 base::Bind(&Timer::Create, Timer::TYPE_ONE_SHOT))
+      .SetMethod("createRepeating",
+                 base::Bind(&Timer::Create, Timer::TYPE_REPEATING));
+}
+
+}  // namespace gin
diff --git a/gin/modules/timer.h b/gin/modules/timer.h
new file mode 100644
index 0000000..1b7f613
--- /dev/null
+++ b/gin/modules/timer.h
@@ -0,0 +1,65 @@
+// Copyright 2013 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 GIN_MODULES_TIMER_H_
+#define GIN_MODULES_TIMER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "gin/gin_export.h"
+#include "gin/handle.h"
+#include "gin/runner.h"
+#include "gin/wrappable.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class ObjectTemplateBuilder;
+
+// A simple scriptable timer that can work in one-shot or repeating mode.
+class GIN_EXPORT Timer : public Wrappable<Timer> {
+ public:
+  enum TimerType {
+    TYPE_ONE_SHOT,
+    TYPE_REPEATING
+  };
+
+  static WrapperInfo kWrapperInfo;
+  static Handle<Timer> Create(TimerType type, v8::Isolate* isolate,
+                              int delay_ms, v8::Local<v8::Function> function);
+
+  ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) override;
+
+ private:
+  Timer(v8::Isolate* isolate, bool repeating, int delay_ms,
+        v8::Local<v8::Function> function);
+  ~Timer() override;
+  void OnTimerFired();
+
+  base::Timer timer_;
+  base::WeakPtr<gin::Runner> runner_;
+  base::WeakPtrFactory<Timer> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(Timer);
+};
+
+
+class GIN_EXPORT TimerModule : public Wrappable<TimerModule> {
+ public:
+  static const char kName[];
+  static WrapperInfo kWrapperInfo;
+  static Handle<TimerModule> Create(v8::Isolate* isolate);
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+
+ private:
+  TimerModule();
+  ~TimerModule() override;
+
+  ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) override;
+};
+
+}  // namespace gin
+
+#endif  // GIN_MODULES_TIMER_H_
diff --git a/gin/modules/timer_unittest.cc b/gin/modules/timer_unittest.cc
new file mode 100644
index 0000000..2490d23
--- /dev/null
+++ b/gin/modules/timer_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2013 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 "gin/modules/timer.h"
+
+#include <memory>
+
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "gin/handle.h"
+#include "gin/object_template_builder.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/shell_runner.h"
+#include "gin/test/v8_test.h"
+#include "gin/try_catch.h"
+#include "gin/wrappable.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+
+namespace {
+
+class Result : public Wrappable<Result> {
+ public:
+  static WrapperInfo kWrapperInfo;
+  static Handle<Result> Create(v8::Isolate* isolate) {
+    return CreateHandle(isolate, new Result());
+  }
+
+  int count() const { return count_; }
+  void set_count(int count) { count_ = count; }
+
+  void Quit() { base::RunLoop::QuitCurrentDeprecated(); }
+
+ private:
+  Result() : count_(0) {
+  }
+
+  ~Result() override = default;
+
+  ObjectTemplateBuilder GetObjectTemplateBuilder(
+      v8::Isolate* isolate) override {
+    return Wrappable<Result>::GetObjectTemplateBuilder(isolate)
+        .SetProperty("count", &Result::count, &Result::set_count)
+        .SetMethod("quit", &Result::Quit);
+  }
+
+  int count_;
+};
+
+WrapperInfo Result::kWrapperInfo = { gin::kEmbedderNativeGin };
+
+struct TestHelper {
+  TestHelper(v8::Isolate* isolate)
+      : runner(new ShellRunner(&delegate, isolate)),
+        scope(runner.get()),
+        timer_module(TimerModule::Create(isolate)),
+        result(Result::Create(isolate)) {
+    EXPECT_FALSE(runner->global().IsEmpty());
+    runner->global()->Set(StringToV8(isolate, "timer"),
+                          timer_module->GetWrapper(isolate).ToLocalChecked());
+    runner->global()->Set(StringToV8(isolate, "result"),
+                          result->GetWrapper(isolate).ToLocalChecked());
+  }
+
+  ShellRunnerDelegate delegate;
+  std::unique_ptr<ShellRunner> runner;
+  Runner::Scope scope;
+  Handle<TimerModule> timer_module;
+  Handle<Result> result;
+};
+
+}  // namespace
+
+typedef V8Test TimerUnittest;
+
+TEST_F(TimerUnittest, OneShot) {
+  TestHelper helper(instance_->isolate());
+  std::string source =
+     "timer.createOneShot(0, function() {"
+     "  result.count++;"
+     "});";
+
+  helper.runner->Run(source, "script");
+  EXPECT_EQ(0, helper.result->count());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, helper.result->count());
+}
+
+TEST_F(TimerUnittest, OneShotCancel) {
+  TestHelper helper(instance_->isolate());
+  std::string source =
+     "var t = timer.createOneShot(0, function() {"
+     "  result.count++;"
+     "});"
+     "t.cancel()";
+
+  helper.runner->Run(source, "script");
+  EXPECT_EQ(0, helper.result->count());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, helper.result->count());
+}
+
+TEST_F(TimerUnittest, Repeating) {
+  TestHelper helper(instance_->isolate());
+
+  // TODO(aa): Cannot do: if (++result.count == 3) because of v8 bug. Create
+  // test case and report.
+  std::string source =
+      "var t = timer.createRepeating(0, function() {"
+      "  result.count++;"
+      "  if (result.count == 3) {"
+      "    /* Cancel the timer to prevent a hang when ScopedTaskEnvironment "
+      "       flushes main thread tasks. */"
+      "    t.cancel();"
+      "    result.quit();"
+      "  }"
+      "});";
+
+  helper.runner->Run(source, "script");
+  EXPECT_EQ(0, helper.result->count());
+
+  base::RunLoop().Run();
+  EXPECT_EQ(3, helper.result->count());
+}
+
+TEST_F(TimerUnittest, TimerCallbackToDestroyedRunner) {
+  TestHelper helper(instance_->isolate());
+  std::string source =
+     "timer.createOneShot(0, function() {"
+     "  result.count++;"
+     "});";
+
+  helper.runner->Run(source, "script");
+  EXPECT_EQ(0, helper.result->count());
+
+  // Destroy runner, which should destroy the timer object we created.
+  helper.runner.reset(NULL);
+  base::RunLoop().RunUntilIdle();
+
+  // Timer should not have run because it was deleted.
+  EXPECT_EQ(0, helper.result->count());
+}
+
+}  // namespace gin
diff --git a/gin/runner.h b/gin/runner.h
index 928275d5..a898b03 100644
--- a/gin/runner.h
+++ b/gin/runner.h
@@ -25,6 +25,10 @@
   // context by creating an instance of Runner::Scope on the stack.
   virtual void Run(const std::string& source,
                    const std::string& resource_name) = 0;
+  virtual v8::Local<v8::Value> Call(v8::Local<v8::Function> function,
+                                     v8::Local<v8::Value> receiver,
+                                     int argc,
+                                     v8::Local<v8::Value> argv[]) = 0;
   virtual ContextHolder* GetContextHolder() = 0;
 
   v8::Local<v8::Object> global() {
diff --git a/gin/shell/gin_main.cc b/gin/shell/gin_main.cc
index 12c74b8..a28ca155 100644
--- a/gin/shell/gin_main.cc
+++ b/gin/shell/gin_main.cc
@@ -18,9 +18,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "gin/array_buffer.h"
 #include "gin/modules/console.h"
-#include "gin/object_template_builder.h"
+#include "gin/modules/module_runner_delegate.h"
 #include "gin/public/isolate_holder.h"
-#include "gin/shell_runner.h"
 #include "gin/try_catch.h"
 #include "gin/v8_initializer.h"
 
@@ -41,20 +40,20 @@
   runner->Run(Load(path), path.AsUTF8Unsafe());
 }
 
-class GinShellRunnerDelegate : public ShellRunnerDelegate {
- public:
-  GinShellRunnerDelegate() {}
+std::vector<base::FilePath> GetModuleSearchPaths() {
+  std::vector<base::FilePath> module_base(1);
+  CHECK(base::GetCurrentDirectory(&module_base[0]));
+  return module_base;
+}
 
-  v8::Local<v8::ObjectTemplate> GetGlobalTemplate(
-      ShellRunner* runner,
-      v8::Isolate* isolate) override {
-    v8::Local<v8::ObjectTemplate> templ =
-        ObjectTemplateBuilder(isolate).Build();
-    gin::Console::Register(isolate, templ);
-    return templ;
+class GinShellRunnerDelegate : public ModuleRunnerDelegate {
+ public:
+  GinShellRunnerDelegate() : ModuleRunnerDelegate(GetModuleSearchPaths()) {
+    AddBuiltinModule(Console::kModuleName, Console::GetModule);
   }
 
   void UnhandledException(ShellRunner* runner, TryCatch& try_catch) override {
+    ModuleRunnerDelegate::UnhandledException(runner, try_catch);
     LOG(ERROR) << try_catch.GetStackTrace();
   }
 
diff --git a/gin/shell/hello_world.js b/gin/shell/hello_world.js
index 6f091000..7216fbd1 100644
--- a/gin/shell/hello_world.js
+++ b/gin/shell/hello_world.js
@@ -2,4 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-log("Hello World");
+define(["console"], function(console) {
+  console.log("Hello World");
+});
diff --git a/gin/shell_runner.cc b/gin/shell_runner.cc
index f052428..b98240a1 100644
--- a/gin/shell_runner.cc
+++ b/gin/shell_runner.cc
@@ -5,6 +5,7 @@
 #include "gin/shell_runner.h"
 
 #include "gin/converter.h"
+#include "gin/modules/module_registry.h"
 #include "gin/per_context_data.h"
 #include "gin/public/context_holder.h"
 #include "gin/try_catch.h"
@@ -75,6 +76,24 @@
   Run(script);
 }
 
+v8::Local<v8::Value> ShellRunner::Call(v8::Local<v8::Function> function,
+                                        v8::Local<v8::Value> receiver,
+                                        int argc,
+                                        v8::Local<v8::Value> argv[]) {
+  TryCatch try_catch(GetContextHolder()->isolate());
+  delegate_->WillRunScript(this);
+
+  auto maybe_result =
+      function->Call(GetContextHolder()->context(), receiver, argc, argv);
+
+  delegate_->DidRunScript(this);
+  v8::Local<v8::Value> result;
+  if (!maybe_result.ToLocal(&result))
+    delegate_->UnhandledException(this, try_catch);
+
+  return result;
+}
+
 ContextHolder* ShellRunner::GetContextHolder() {
   return context_holder_.get();
 }
diff --git a/gin/shell_runner.h b/gin/shell_runner.h
index 10c92df..f7651f0 100644
--- a/gin/shell_runner.h
+++ b/gin/shell_runner.h
@@ -48,6 +48,10 @@
   // Runner overrides:
   void Run(const std::string& source,
            const std::string& resource_name) override;
+  v8::Local<v8::Value> Call(v8::Local<v8::Function> function,
+                             v8::Local<v8::Value> receiver,
+                             int argc,
+                             v8::Local<v8::Value> argv[]) override;
   ContextHolder* GetContextHolder() override;
 
  private:
diff --git a/gin/shell_runner_unittest.cc b/gin/shell_runner_unittest.cc
index a2dc656f..65a7db9 100644
--- a/gin/shell_runner_unittest.cc
+++ b/gin/shell_runner_unittest.cc
@@ -24,7 +24,8 @@
 
 namespace gin {
 
-TEST(RunnerTest, Run) {
+// TODO(yzshen): crbug.com/793480
+TEST(RunnerTest, DISABLED_Run) {
   base::test::ScopedTaskEnvironment scoped_task_environment;
   std::string source = "this.result = 'PASS';\n";
 
diff --git a/gin/test/expect.js b/gin/test/expect.js
new file mode 100644
index 0000000..597b5b1
--- /dev/null
+++ b/gin/test/expect.js
@@ -0,0 +1,299 @@
+// Copyright 2013 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.
+
+define(function() {
+  // Equality function based on isEqual in
+  // Underscore.js 1.5.2
+  // http://underscorejs.org
+  // (c) 2009-2013 Jeremy Ashkenas,
+  //               DocumentCloud,
+  //               and Investigative Reporters & Editors
+  // Underscore may be freely distributed under the MIT license.
+  //
+  function has(obj, key) {
+    return obj.hasOwnProperty(key);
+  }
+  function isFunction(obj) {
+    return typeof obj === 'function';
+  }
+  function isArrayBufferClass(className) {
+    return className == '[object ArrayBuffer]' ||
+        className.match(/\[object \w+\d+(Clamped)?Array\]/);
+  }
+  // Internal recursive comparison function for `isEqual`.
+  function eq(a, b, aStack, bStack) {
+    // Identical objects are equal. `0 === -0`, but they aren't identical.
+    // See the Harmony `egal` proposal:
+    // http://wiki.ecmascript.org/doku.php?id=harmony:egal.
+    if (a === b)
+      return a !== 0 || 1 / a == 1 / b;
+    // A strict comparison is necessary because `null == undefined`.
+    if (a == null || b == null)
+      return a === b;
+    // Compare `[[Class]]` names.
+    var className = toString.call(a);
+    if (className != toString.call(b))
+      return false;
+    switch (className) {
+      // Strings, numbers, dates, and booleans are compared by value.
+      case '[object String]':
+        // Primitives and their corresponding object wrappers are equivalent;
+        // thus, `"5"` is equivalent to `new String("5")`.
+        return a == String(b);
+      case '[object Number]':
+        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is
+        // performed for other numeric values.
+        return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+      case '[object Date]':
+      case '[object Boolean]':
+        // Coerce dates and booleans to numeric primitive values. Dates are
+        // compared by their millisecond representations. Note that invalid
+        // dates with millisecond representations of `NaN` are not equivalent.
+        return +a == +b;
+      // RegExps are compared by their source patterns and flags.
+      case '[object RegExp]':
+        return a.source == b.source &&
+               a.global == b.global &&
+               a.multiline == b.multiline &&
+               a.ignoreCase == b.ignoreCase;
+    }
+    if (typeof a != 'object' || typeof b != 'object')
+      return false;
+    // Assume equality for cyclic structures. The algorithm for detecting
+    // cyclic structures is adapted from ES 5.1 section 15.12.3, abstract
+    // operation `JO`.
+    var length = aStack.length;
+    while (length--) {
+      // Linear search. Performance is inversely proportional to the number of
+      // unique nested structures.
+      if (aStack[length] == a)
+        return bStack[length] == b;
+    }
+    // Objects with different constructors are not equivalent, but `Object`s
+    // from different frames are.
+    var aCtor = a.constructor, bCtor = b.constructor;
+    if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
+                             isFunction(bCtor) && (bCtor instanceof bCtor))
+                        && ('constructor' in a && 'constructor' in b)) {
+      return false;
+    }
+    // Add the first object to the stack of traversed objects.
+    aStack.push(a);
+    bStack.push(b);
+    var size = 0, result = true;
+    // Recursively compare Maps, objects and arrays.
+    if (className == '[object Array]' || isArrayBufferClass(className)) {
+      // Compare array lengths to determine if a deep comparison is necessary.
+      size = a.length;
+      result = size == b.length;
+      if (result) {
+        // Deep compare the contents, ignoring non-numeric properties.
+        while (size--) {
+          if (!(result = eq(a[size], b[size], aStack, bStack)))
+            break;
+        }
+      }
+    } else if (className == '[object Map]') {
+      result = a.size == b.size;
+      if (result) {
+        var entries = a.entries();
+        for (var e = entries.next(); result && !e.done; e = entries.next()) {
+          var key = e.value[0];
+          var value = e.value[1];
+          result = b.has(key) && eq(value, b.get(key), aStack, bStack);
+        }
+      }
+    } else {
+      // Deep compare objects.
+      for (var key in a) {
+        if (has(a, key)) {
+          // Count the expected number of properties.
+          size++;
+          // Deep compare each member.
+          if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack)))
+            break;
+        }
+      }
+      // Ensure that both objects contain the same number of properties.
+      if (result) {
+        for (key in b) {
+          if (has(b, key) && !(size--))
+            break;
+        }
+        result = !size;
+      }
+    }
+    // Remove the first object from the stack of traversed objects.
+    aStack.pop();
+    bStack.pop();
+    return result;
+  };
+
+  function describe(subjects) {
+    var descriptions = [];
+    Object.getOwnPropertyNames(subjects).forEach(function(name) {
+      if (name === "Description")
+        descriptions.push(subjects[name]);
+      else
+        descriptions.push(name + ": " + JSON.stringify(subjects[name]));
+    });
+    return descriptions.join(" ");
+  }
+
+  var predicates = {};
+
+  predicates.toBe = function(actual, expected) {
+    return {
+      "result": actual === expected,
+      "message": describe({
+        "Actual": actual,
+        "Expected": expected,
+      }),
+    };
+  };
+
+  predicates.toEqual = function(actual, expected) {
+    return {
+      "result": eq(actual, expected, [], []),
+      "message": describe({
+        "Actual": actual,
+        "Expected": expected,
+      }),
+    };
+  };
+
+  predicates.toBeDefined = function(actual) {
+    return {
+      "result": typeof actual !== "undefined",
+      "message": describe({
+        "Actual": actual,
+        "Description": "Expected a defined value",
+      }),
+    };
+  };
+
+  predicates.toBeUndefined = function(actual) {
+    // Recall: undefined is just a global variable. :)
+    return {
+      "result": typeof actual === "undefined",
+      "message": describe({
+        "Actual": actual,
+        "Description": "Expected an undefined value",
+      }),
+    };
+  };
+
+  predicates.toBeNull = function(actual) {
+    // Recall: typeof null === "object".
+    return {
+      "result": actual === null,
+      "message": describe({
+        "Actual": actual,
+        "Expected": null,
+      }),
+    };
+  };
+
+  predicates.toBeTruthy = function(actual) {
+    return {
+      "result": !!actual,
+      "message": describe({
+        "Actual": actual,
+        "Description": "Expected a truthy value",
+      }),
+    };
+  };
+
+  predicates.toBeFalsy = function(actual) {
+    return {
+      "result": !!!actual,
+      "message": describe({
+        "Actual": actual,
+        "Description": "Expected a falsy value",
+      }),
+    };
+  };
+
+  predicates.toContain = function(actual, element) {
+    return {
+      "result": (function () {
+        for (var i = 0; i < actual.length; ++i) {
+          if (eq(actual[i], element, [], []))
+            return true;
+        }
+        return false;
+      })(),
+      "message": describe({
+        "Actual": actual,
+        "Element": element,
+      }),
+    };
+  };
+
+  predicates.toBeLessThan = function(actual, reference) {
+    return {
+      "result": actual < reference,
+      "message": describe({
+        "Actual": actual,
+        "Reference": reference,
+      }),
+    };
+  };
+
+  predicates.toBeGreaterThan = function(actual, reference) {
+    return {
+      "result": actual > reference,
+      "message": describe({
+        "Actual": actual,
+        "Reference": reference,
+      }),
+    };
+  };
+
+  predicates.toThrow = function(actual) {
+    return {
+      "result": (function () {
+        if (!isFunction(actual))
+          throw new TypeError;
+        try {
+          actual();
+        } catch (ex) {
+          return true;
+        }
+        return false;
+      })(),
+      "message": "Expected function to throw",
+    };
+  }
+
+  function negate(predicate) {
+    return function() {
+      var outcome = predicate.apply(null, arguments);
+      outcome.result = !outcome.result;
+      return outcome;
+    }
+  }
+
+  function check(predicate) {
+    return function() {
+      var outcome = predicate.apply(null, arguments);
+      if (outcome.result)
+        return;
+      throw outcome.message;
+    };
+  }
+
+  function Condition(actual) {
+    this.not = {};
+    Object.getOwnPropertyNames(predicates).forEach(function(name) {
+      var bound = predicates[name].bind(null, actual);
+      this[name] = check(bound);
+      this.not[name] = check(negate(bound));
+    }, this);
+  }
+
+  return function(actual) {
+    return new Condition(actual);
+  };
+});
diff --git a/gin/test/file.cc b/gin/test/file.cc
new file mode 100644
index 0000000..38efbb4
--- /dev/null
+++ b/gin/test/file.cc
@@ -0,0 +1,88 @@
+// 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 "gin/test/file.h"
+
+#include <iostream>
+
+#include "base/bind.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+
+using v8::ObjectTemplate;
+
+namespace gin {
+
+namespace {
+
+v8::Local<v8::Value> ReadFileToString(gin::Arguments* args) {
+  std::string filename;
+  if (!args->GetNext(&filename))
+    return v8::Null(args->isolate());
+
+  const base::FilePath& path = base::FilePath::FromUTF8Unsafe(filename);
+  std::string contents;
+  if (!ReadFileToString(path, &contents))
+    return v8::Null(args->isolate());
+
+  return gin::Converter<std::string>::ToV8(args->isolate(), contents);
+}
+
+v8::Local<v8::Value> GetSourceRootDirectory(gin::Arguments* args) {
+  base::FilePath path;
+  if (!PathService::Get(base::DIR_SOURCE_ROOT, &path))
+    return v8::Null(args->isolate());
+  return gin::Converter<std::string>::ToV8(args->isolate(),
+                                           path.AsUTF8Unsafe());
+}
+
+v8::Local<v8::Value> GetFilesInDirectory(gin::Arguments* args) {
+  std::string filename;
+  if (!args->GetNext(&filename))
+    return v8::Null(args->isolate());
+
+  const base::FilePath& path = base::FilePath::FromUTF8Unsafe(filename);
+  if (!base::DirectoryExists(path))
+    return v8::Null(args->isolate());
+
+  std::vector<std::string> names;
+  base::FileEnumerator e(path, false, base::FileEnumerator::FILES);
+  for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) {
+    names.push_back(name.BaseName().AsUTF8Unsafe());
+  }
+
+  v8::Local<v8::Value> v8_names;
+  if (!TryConvertToV8(args->isolate(), names, &v8_names))
+    return v8::Null(args->isolate());
+  return v8_names;
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+}  // namespace
+
+const char File::kModuleName[] = "file";
+
+v8::Local<v8::Value> File::GetModule(v8::Isolate* isolate) {
+  gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+  v8::Local<ObjectTemplate> templ = data->GetObjectTemplate(&g_wrapper_info);
+  if (templ.IsEmpty()) {
+    templ = gin::ObjectTemplateBuilder(isolate)
+        .SetMethod("readFileToString", ReadFileToString)
+        .SetMethod("getFilesInDirectory", GetFilesInDirectory)
+        .SetMethod("getSourceRootDirectory", GetSourceRootDirectory)
+        .Build();
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+  return templ->NewInstance();
+}
+
+}  // namespace gin
diff --git a/gin/test/file.h b/gin/test/file.h
new file mode 100644
index 0000000..a4acd59
--- /dev/null
+++ b/gin/test/file.h
@@ -0,0 +1,21 @@
+// 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 GIN_TEST_FILE_H_
+#define GIN_TEST_FILE_H_
+
+#include "v8/include/v8.h"
+
+namespace gin {
+
+class File {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+}  // namespace gin
+
+#endif  // GIN_TEST_FILE_H_
+
diff --git a/gin/test/file_runner.cc b/gin/test/file_runner.cc
new file mode 100644
index 0000000..39ece62
--- /dev/null
+++ b/gin/test/file_runner.cc
@@ -0,0 +1,94 @@
+// Copyright 2013 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 "gin/test/file_runner.h"
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "gin/array_buffer.h"
+#include "gin/converter.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/public/context_holder.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/test/file.h"
+#include "gin/test/gc.h"
+#include "gin/test/gtest.h"
+#include "gin/try_catch.h"
+#include "gin/v8_initializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+
+namespace {
+
+std::vector<base::FilePath> GetModuleSearchPaths() {
+  std::vector<base::FilePath> search_paths(2);
+  PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]);
+  PathService::Get(base::DIR_EXE, &search_paths[1]);
+  search_paths[1] = search_paths[1].AppendASCII("gen");
+  return search_paths;
+}
+
+}  // namespace
+
+FileRunnerDelegate::FileRunnerDelegate()
+    : ModuleRunnerDelegate(GetModuleSearchPaths()) {
+  AddBuiltinModule(Console::kModuleName, Console::GetModule);
+  AddBuiltinModule(GTest::kModuleName, GTest::GetModule);
+  AddBuiltinModule(GC::kModuleName, GC::GetModule);
+  AddBuiltinModule(File::kModuleName, File::GetModule);
+}
+
+FileRunnerDelegate::~FileRunnerDelegate() = default;
+
+void FileRunnerDelegate::UnhandledException(ShellRunner* runner,
+                                            TryCatch& try_catch) {
+  ModuleRunnerDelegate::UnhandledException(runner, try_catch);
+  FAIL() << try_catch.GetStackTrace();
+}
+
+void RunTestFromFile(const base::FilePath& path, FileRunnerDelegate* delegate,
+                     bool run_until_idle) {
+  ASSERT_TRUE(base::PathExists(path)) << path.LossyDisplayName();
+  std::string source;
+  ASSERT_TRUE(ReadFileToString(path, &source));
+
+  base::test::ScopedTaskEnvironment scoped_task_environment;
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  gin::V8Initializer::LoadV8Snapshot();
+  gin::V8Initializer::LoadV8Natives();
+#ifdef USE_V8_CONTEXT_SNAPSHOT
+  gin::V8Initializer::LoadV8ContextSnapshot();
+#endif  // USE_V8_CONTEXT_SNAPSHOT
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+
+  gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+                                 gin::IsolateHolder::kStableV8Extras,
+                                 gin::ArrayBufferAllocator::SharedInstance());
+
+  gin::IsolateHolder instance(base::ThreadTaskRunnerHandle::Get());
+  gin::ShellRunner runner(delegate, instance.isolate());
+  {
+    gin::Runner::Scope scope(&runner);
+    instance.isolate()->SetCaptureStackTraceForUncaughtExceptions(true);
+    runner.Run(source, path.AsUTF8Unsafe());
+
+    if (run_until_idle) {
+      base::RunLoop().RunUntilIdle();
+    } else {
+      base::RunLoop().Run();
+    }
+
+    v8::Local<v8::Value> result = runner.global()->Get(
+        StringToSymbol(runner.GetContextHolder()->isolate(), "result"));
+    EXPECT_EQ("PASS", V8ToString(result));
+  }
+}
+
+}  // namespace gin
diff --git a/gin/test/file_runner.h b/gin/test/file_runner.h
new file mode 100644
index 0000000..b20859a
--- /dev/null
+++ b/gin/test/file_runner.h
@@ -0,0 +1,38 @@
+// Copyright 2013 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 GIN_TEST_FILE_RUNNER_H_
+#define GIN_TEST_FILE_RUNNER_H_
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "gin/modules/module_runner_delegate.h"
+#include "gin/runner.h"
+
+namespace gin {
+
+// FileRunnerDelegate is a simple RunnerDelegate that's useful for running
+// tests. The FileRunnerDelegate provides built-in modules for "console" and
+// "gtest" that are useful when writing unit tests.
+//
+// TODO(abarth): Rename FileRunnerDelegate to TestRunnerDelegate.
+class FileRunnerDelegate : public ModuleRunnerDelegate {
+ public:
+  FileRunnerDelegate();
+  ~FileRunnerDelegate() override;
+
+ private:
+  // From ModuleRunnerDelegate:
+  void UnhandledException(ShellRunner* runner, TryCatch& try_catch) override;
+
+  DISALLOW_COPY_AND_ASSIGN(FileRunnerDelegate);
+};
+
+void RunTestFromFile(const base::FilePath& path, FileRunnerDelegate* delegate,
+                     bool run_until_idle = true);
+
+}  // namespace gin
+
+#endif  // GIN_TEST_FILE_RUNNER_H_
diff --git a/gin/test/file_unittests.js b/gin/test/file_unittests.js
new file mode 100644
index 0000000..8c25806
--- /dev/null
+++ b/gin/test/file_unittests.js
@@ -0,0 +1,37 @@
+// 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.
+
+define([
+    "gin/test/expect",
+    "file"
+  ], function(expect, file) {
+
+  function isString(x) {
+    return toString.call(x) === '[object String]'
+  }
+
+  var rootDir = file.getSourceRootDirectory();
+  expect(isString(rootDir)).toBeTruthy();
+
+  var noArgsNull = file.getFilesInDirectory();
+  expect(noArgsNull).toBeNull();
+
+  var files = file.getFilesInDirectory(rootDir);
+  expect(Array.isArray(files)).toBeTruthy();
+
+  var nsdNull = file.getFilesInDirectory(rootDir + "/no_such_dir");
+  expect(nsdNull).toBeNull();
+
+  var owners = file.readFileToString(rootDir + "/OWNERS");
+  expect(isString(owners)).toBeTruthy();
+  expect(owners.length).toBeGreaterThan(0);
+
+  noArgsNull = file.readFileToString();
+  expect(noArgsNull).toBeNull();
+
+  var nsfNull = file.readFileToString(rootDir + "/no_such_file");
+  expect(nsfNull).toBeNull();
+
+  this.result = "PASS";
+});
diff --git a/gin/test/gc.cc b/gin/test/gc.cc
new file mode 100644
index 0000000..4cd67e1
--- /dev/null
+++ b/gin/test/gc.cc
@@ -0,0 +1,41 @@
+// 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 "gin/test/gc.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/function_template.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/wrappable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+
+namespace {
+WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
+}  // namespace
+
+const char GC::kModuleName[] = "gc";
+
+v8::Local<v8::Value> GC::GetModule(v8::Isolate* isolate) {
+  PerIsolateData* data = PerIsolateData::From(isolate);
+  v8::Local<v8::ObjectTemplate> templ =
+      data->GetObjectTemplate(&g_wrapper_info);
+  if (templ.IsEmpty()) {
+    templ = ObjectTemplateBuilder(isolate)
+        .SetMethod("collectGarbage",
+                   base::Bind(&v8::Isolate::LowMemoryNotification,
+                              base::Unretained(isolate)))
+        .Build();
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+  return templ->NewInstance();
+}
+
+}  // namespace gin
diff --git a/gin/test/gc.h b/gin/test/gc.h
new file mode 100644
index 0000000..25917ef
--- /dev/null
+++ b/gin/test/gc.h
@@ -0,0 +1,21 @@
+// 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 GIN_TEST_GC_H_
+#define GIN_TEST_GC_H_
+
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// This module provides bindings to the garbage collector.
+class GC {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+}  // namespace gin
+
+#endif  // GIN_TEST_GC_H_
diff --git a/gin/test/gtest.cc b/gin/test/gtest.cc
new file mode 100644
index 0000000..76aaf1f
--- /dev/null
+++ b/gin/test/gtest.cc
@@ -0,0 +1,62 @@
+// Copyright 2013 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 "gin/test/gtest.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/function_template.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/wrappable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+
+namespace {
+
+void Fail(const std::string& description) {
+  FAIL() << description;
+}
+
+void ExpectTrue(bool condition, const std::string& description) {
+  EXPECT_TRUE(condition) << description;
+}
+
+void ExpectFalse(bool condition, const std::string& description) {
+  EXPECT_FALSE(condition) << description;
+}
+
+void ExpectEqual(const v8::Local<v8::Value> expected,
+                 const v8::Local<v8::Value> actual,
+                 const std::string& description) {
+  EXPECT_TRUE(expected->StrictEquals(actual)) << description;
+}
+
+WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
+
+}  // namespace
+
+const char GTest::kModuleName[] = "gtest";
+
+v8::Local<v8::Value> GTest::GetModule(v8::Isolate* isolate) {
+  PerIsolateData* data = PerIsolateData::From(isolate);
+  v8::Local<v8::ObjectTemplate> templ =
+      data->GetObjectTemplate(&g_wrapper_info);
+  if (templ.IsEmpty()) {
+    templ = ObjectTemplateBuilder(isolate)
+        .SetMethod("fail", Fail)
+        .SetMethod("expectTrue", ExpectTrue)
+        .SetMethod("expectFalse", ExpectFalse)
+        .SetMethod("expectEqual", ExpectEqual)
+        .Build();
+    data->SetObjectTemplate(&g_wrapper_info, templ);
+  }
+  return templ->NewInstance();
+}
+
+}  // namespace gin
diff --git a/gin/test/gtest.h b/gin/test/gtest.h
new file mode 100644
index 0000000..8f4332d0
--- /dev/null
+++ b/gin/test/gtest.h
@@ -0,0 +1,23 @@
+// Copyright 2013 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 GIN_TEST_GTEST_H_
+#define GIN_TEST_GTEST_H_
+
+#include "v8/include/v8.h"
+
+namespace gin {
+
+// This module provides bindings to gtest. Most tests should use an idiomatic
+// JavaScript testing API, but this module is available for tests that need a
+// low-level integration with gtest.
+class GTest {
+ public:
+  static const char kModuleName[];
+  static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+}  // namespace gin
+
+#endif  // GIN_TEST_GTEST_H_
diff --git a/gin/test/gtest_unittests.js b/gin/test/gtest_unittests.js
new file mode 100644
index 0000000..1d566d5
--- /dev/null
+++ b/gin/test/gtest_unittests.js
@@ -0,0 +1,11 @@
+// Copyright 2013 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.
+
+define(["gtest"], function(gtest) {
+  gtest.expectTrue(true, "true is true");
+  gtest.expectFalse(false, "false is false");
+  gtest.expectTrue(this, "this is " + this);
+
+  this.result = "PASS";
+});
diff --git a/gin/test/run_js_tests.cc b/gin/test/run_js_tests.cc
new file mode 100644
index 0000000..b83dc9f2
--- /dev/null
+++ b/gin/test/run_js_tests.cc
@@ -0,0 +1,43 @@
+// Copyright 2013 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/files/file_util.h"
+#include "base/path_service.h"
+#include "gin/test/file_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gin {
+namespace {
+
+base::FilePath BasePath() {
+  base::FilePath path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("gin");
+}
+
+void RunTest(const base::FilePath& path) {
+  FileRunnerDelegate delegate;
+  RunTestFromFile(path, &delegate);
+}
+
+TEST(JSTest, File) {
+  RunTest(BasePath()
+          .AppendASCII("test")
+          .AppendASCII("file_unittests.js"));
+}
+
+TEST(JSTest, GTest) {
+  RunTest(BasePath()
+          .AppendASCII("test")
+          .AppendASCII("gtest_unittests.js"));
+}
+
+TEST(JSTest, ModuleRegistry) {
+  RunTest(BasePath()
+          .AppendASCII("modules")
+          .AppendASCII("module_registry_unittests.js"));
+}
+
+}  // namespace
+}  // gin
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 7c512768..0831eed9 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -2268,6 +2268,17 @@
 
 - (void)presentNewTabTipBubble {
   DCHECK(self.browserState);
+  // If the BVC is not visible, do not present the bubble.
+  if (!self.viewVisible)
+    return;
+  // Do not present the bubble if there is no current or if the current tab is
+  // the NTP.
+  Tab* currentTab = [self.tabModel currentTab];
+  if (!currentTab)
+    return;
+  if (currentTab.webState->GetVisibleURL() == kChromeUINewTabURL)
+    return;
+
   NSString* text =
       l10n_util::GetNSStringWithFixup(IDS_IOS_NEW_TAB_IPH_PROMOTION_TEXT);
   CGPoint tabSwitcherAnchor;
@@ -2317,6 +2328,10 @@
   DCHECK(self.browserState);
   DCHECK([_toolbarCoordinator
       respondsToSelector:@selector(anchorPointForToolsMenuButton:)]);
+  // If the BVC is not visible, do not present the bubble.
+  if (!self.viewVisible)
+    return;
+
   NSString* text = l10n_util::GetNSStringWithFixup(
       IDS_IOS_NEW_INCOGNITO_TAB_IPH_PROMOTION_TEXT);
   CGPoint toolsButtonAnchor = [_toolbarCoordinator
diff --git a/ios/chrome/browser/ui/fullscreen/BUILD.gn b/ios/chrome/browser/ui/fullscreen/BUILD.gn
index 243bd524..820772ff 100644
--- a/ios/chrome/browser/ui/fullscreen/BUILD.gn
+++ b/ios/chrome/browser/ui/fullscreen/BUILD.gn
@@ -53,6 +53,8 @@
     "fullscreen_web_state_list_observer.mm",
     "fullscreen_web_state_observer.h",
     "fullscreen_web_state_observer.mm",
+    "voice_over_fullscreen_disabler.h",
+    "voice_over_fullscreen_disabler.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller.h b/ios/chrome/browser/ui/fullscreen/fullscreen_controller.h
index 5d89062..2ea797d1 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller.h
@@ -17,6 +17,7 @@
 class FullscreenMediator;
 class FullscreenModel;
 class FullscreenWebStateListObserver;
+@class VoiceOverFullscreenDisabler;
 class WebStateList;
 
 // An object that observes scrolling events in the main content area and
@@ -71,6 +72,8 @@
   std::unique_ptr<FullscreenModel> model_;
   // The bridge used to forward brodcasted UI to |model_|.
   __strong ChromeBroadcastOberverBridge* bridge_ = nil;
+  // A helper object that disables fullscreen when VoiceOver is enabled.
+  __strong VoiceOverFullscreenDisabler* voice_over_disabler_ = nil;
   // Object that manages sending signals to FullscreenControllerObservers.
   std::unique_ptr<FullscreenMediator> mediator_;
   // A WebStateListObserver that updates |model_| for WebStateList changes.
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_controller.mm
index f6425d65..6633595f 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_mediator.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_model.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_web_state_list_observer.h"
+#import "ios/chrome/browser/ui/fullscreen/voice_over_fullscreen_disabler.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -20,6 +21,8 @@
       model_(base::MakeUnique<FullscreenModel>()),
       bridge_(
           [[ChromeBroadcastOberverBridge alloc] initWithObserver:model_.get()]),
+      voice_over_disabler_(
+          [[VoiceOverFullscreenDisabler alloc] initWithController:this]),
       mediator_(base::MakeUnique<FullscreenMediator>(this, model_.get())) {
   DCHECK(broadcaster_);
   [broadcaster_ addObserver:bridge_
@@ -67,6 +70,7 @@
 
 void FullscreenController::Shutdown() {
   mediator_->Disconnect();
+  [voice_over_disabler_ disconnect];
   if (web_state_list_observer_)
     web_state_list_observer_->Disconnect();
   [broadcaster_ removeObserver:bridge_
diff --git a/ios/chrome/browser/ui/fullscreen/voice_over_fullscreen_disabler.h b/ios/chrome/browser/ui/fullscreen/voice_over_fullscreen_disabler.h
new file mode 100644
index 0000000..df77e9e
--- /dev/null
+++ b/ios/chrome/browser/ui/fullscreen/voice_over_fullscreen_disabler.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_FULLSCREEN_VOICE_OVER_FULLSCREEN_DISABLER_H_
+#define IOS_CHROME_BROWSER_UI_FULLSCREEN_VOICE_OVER_FULLSCREEN_DISABLER_H_
+
+#import <UIKit/UIKit.h>
+
+class FullscreenController;
+
+// Helper class that handles disabling fullscreen while VoiceOver is enabled.
+@interface VoiceOverFullscreenDisabler : NSObject
+
+- (nullable instancetype)initWithController:
+    (nonnull FullscreenController*)controller NS_DESIGNATED_INITIALIZER;
+- (nullable instancetype)init NS_UNAVAILABLE;
+
+// Stops observing VoiceOver notifications.
+- (void)disconnect;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_FULLSCREEN_VOICE_OVER_FULLSCREEN_DISABLER_H_
diff --git a/ios/chrome/browser/ui/fullscreen/voice_over_fullscreen_disabler.mm b/ios/chrome/browser/ui/fullscreen/voice_over_fullscreen_disabler.mm
new file mode 100644
index 0000000..7cae7336
--- /dev/null
+++ b/ios/chrome/browser/ui/fullscreen/voice_over_fullscreen_disabler.mm
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/fullscreen/voice_over_fullscreen_disabler.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
+#import "ios/chrome/browser/ui/fullscreen/scoped_fullscreen_disabler.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface VoiceOverFullscreenDisabler () {
+  // The disabler created when VoiceOver is enabled.
+  std::unique_ptr<ScopedFullscreenDisabler> _disabler;
+}
+// The FullscreenController being enabled/disabled for VoiceOver.
+@property(nonatomic, readonly, nonnull) FullscreenController* controller;
+// Creates or destroys |_disabler| depending on whether VoiceOver is enabled.
+- (void)voiceOverStatusChanged;
+@end
+
+@implementation VoiceOverFullscreenDisabler
+@synthesize controller = _controller;
+
+- (instancetype)initWithController:(FullscreenController*)controller {
+  if (self = [super init]) {
+    _controller = controller;
+    DCHECK(_controller);
+    if (@available(iOS 11, *)) {
+      [[NSNotificationCenter defaultCenter]
+          addObserver:self
+             selector:@selector(voiceOverStatusChanged)
+                 name:UIAccessibilityVoiceOverStatusDidChangeNotification
+               object:nil];
+    } else {
+      [[NSNotificationCenter defaultCenter]
+          addObserver:self
+             selector:@selector(voiceOverStatusChanged)
+                 name:UIAccessibilityVoiceOverStatusChanged
+               object:nil];
+    }
+    if (UIAccessibilityIsVoiceOverRunning())
+      _disabler = base::MakeUnique<ScopedFullscreenDisabler>(_controller);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  // |-disconnect| should be called before deallocation.
+  DCHECK(!_controller);
+}
+
+#pragma mark Public
+
+- (void)disconnect {
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+  _controller = nullptr;
+}
+
+#pragma mark Private
+
+- (void)voiceOverStatusChanged {
+  _disabler = UIAccessibilityIsVoiceOverRunning()
+                  ? base::MakeUnique<ScopedFullscreenDisabler>(_controller)
+                  : nullptr;
+}
+
+@end
diff --git a/ios/chrome/browser/web/resources/payment_request.js b/ios/chrome/browser/web/resources/payment_request.js
index b36821d1..9c2bf73 100644
--- a/ios/chrome/browser/web/resources/payment_request.js
+++ b/ios/chrome/browser/web/resources/payment_request.js
@@ -180,13 +180,14 @@
    */
   __gCrWeb['paymentRequestManager'].validatePaymentCurrencyAmount = function(
       amount, amountName) {
-    var value = String(amount.value);
-    if (value > __gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
+    // Convert the value to String if it isn't already one.
+    amount.value = String(amount.value);
+    if (amount.value > __gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
       throw new TypeError(
           amountName + ' value cannot be longer than ' +
           __gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH + ' characters');
     }
-    if (!/^-?[0-9]+(\.[0-9]+)?$/.test(value)) {
+    if (!/^-?[0-9]+(\.[0-9]+)?$/.test(amount.value)) {
       throw new TypeError(
           amountName + ' value is not a valid decimal monetary value');
     }
diff --git a/media/base/mime_util_internal.cc b/media/base/mime_util_internal.cc
index 850b909d5..9c929b21 100644
--- a/media/base/mime_util_internal.cc
+++ b/media/base/mime_util_internal.cc
@@ -261,7 +261,7 @@
   const CodecSet ogg_audio_codecs{FLAC, OPUS, VORBIS};
 
 #if !defined(OS_ANDROID)
-  CodecSet ogg_video_codecs{THEORA};
+  CodecSet ogg_video_codecs{THEORA, VP8};
 #else
   CodecSet ogg_video_codecs;
 #endif  // !defined(OS_ANDROID)
@@ -279,32 +279,38 @@
   CodecSet webm_codecs(webm_audio_codecs);
   webm_codecs.insert(webm_video_codecs.begin(), webm_video_codecs.end());
 
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
   const CodecSet mp3_codecs{MP3};
+
+  CodecSet mp4_audio_codecs;
+  mp4_audio_codecs.emplace(MP3);
+  mp4_audio_codecs.emplace(FLAC);
+
+  // Only VP9 with valid codec string vp09.xx.xx.xx.xx.xx.xx.xx is supported.
+  // See ParseVp9CodecID for details.
+  CodecSet mp4_video_codecs;
+  mp4_video_codecs.emplace(VP9);
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
   const CodecSet aac{MPEG2_AAC, MPEG4_AAC};
+  mp4_audio_codecs.insert(aac.begin(), aac.end());
 
   CodecSet avc_and_aac(aac);
   avc_and_aac.emplace(H264);
 
-  CodecSet mp4_audio_codecs(aac);
-  mp4_audio_codecs.emplace(MP3);
-  mp4_audio_codecs.emplace(FLAC);
 #if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
   mp4_audio_codecs.emplace(AC3);
   mp4_audio_codecs.emplace(EAC3);
 #endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
 
-  CodecSet mp4_video_codecs;
   mp4_video_codecs.emplace(H264);
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
   mp4_video_codecs.emplace(HEVC);
 #endif  // BUILDFLAG(ENABLE_HEVC_DEMUXING)
-  // Only VP9 with valid codec string vp09.xx.xx.xx.xx.xx.xx.xx is supported.
-  // See ParseVp9CodecID for details.
-  mp4_video_codecs.emplace(VP9);
+
 #if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
   mp4_video_codecs.emplace(DOLBY_VISION);
 #endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 #if BUILDFLAG(ENABLE_AV1_DECODER)
   if (base::FeatureList::IsEnabled(kAv1Decoder))
     mp4_video_codecs.emplace(AV1);
@@ -312,7 +318,6 @@
 
   CodecSet mp4_codecs(mp4_audio_codecs);
   mp4_codecs.insert(mp4_video_codecs.begin(), mp4_video_codecs.end());
-#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
   const CodecSet implicit_codec;
   AddContainerWithCodecs("audio/wav", wav_codecs, false);
@@ -328,15 +333,15 @@
   // TODO(ddorwin): Should the application type support Opus?
   AddContainerWithCodecs("application/ogg", ogg_codecs, false);
   AddContainerWithCodecs("audio/flac", implicit_codec, false);
+  AddContainerWithCodecs("audio/mpeg", mp3_codecs, false);  // Allow "mp3".
+  AddContainerWithCodecs("audio/mp3", implicit_codec, false);
+  AddContainerWithCodecs("audio/x-mp3", implicit_codec, false);
+  AddContainerWithCodecs("audio/mp4", mp4_audio_codecs, false);
+  DCHECK(!mp4_video_codecs.empty());
+  AddContainerWithCodecs("video/mp4", mp4_codecs, false);
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-  AddContainerWithCodecs("audio/mpeg", mp3_codecs, true);  // Allow "mp3".
-  AddContainerWithCodecs("audio/mp3", implicit_codec, true);
-  AddContainerWithCodecs("audio/x-mp3", implicit_codec, true);
   AddContainerWithCodecs("audio/aac", implicit_codec, true);  // AAC / ADTS.
-  AddContainerWithCodecs("audio/mp4", mp4_audio_codecs, true);
-  DCHECK(!mp4_video_codecs.empty());
-  AddContainerWithCodecs("video/mp4", mp4_codecs, true);
   // These strings are supported for backwards compatibility only and thus only
   // support the codecs needed for compatibility.
   AddContainerWithCodecs("audio/x-m4a", aac, true);
@@ -516,6 +521,19 @@
 void MimeUtil::RemoveProprietaryMediaTypesAndCodecs() {
   for (const auto& container : proprietary_media_containers_)
     media_format_map_.erase(container);
+
+  // TODO(chcunningham): Delete this hack (really this whole test-only method).
+  // This is done as short term workaround for LayoutTests to pass. MP4 is no
+  // longer proprietary, but may still contain proprietary codecs (e.g. AVC).
+  // Many  layout tests only check for container support and may break (absent
+  // this  hack) if run on a non-proprietary build. This mess is being fixed in
+  // https://chromium-review.googlesource.com/c/chromium/src/+/807604
+  media_format_map_.erase("video/mp4");
+  media_format_map_.erase("audio/mp4");
+  media_format_map_.erase("audio/mpeg");
+  media_format_map_.erase("audio/mp3");
+  media_format_map_.erase("audio/x-mp3");
+
   allow_proprietary_codecs_ = false;
 }
 
@@ -970,7 +988,6 @@
     case INVALID_CODEC:
     case AC3:
     case EAC3:
-    case MP3:
     case MPEG2_AAC:
     case MPEG4_AAC:
     case H264:
@@ -978,6 +995,7 @@
     case DOLBY_VISION:
       return true;
 
+    case MP3:
     case PCM:
     case VORBIS:
     case OPUS:
diff --git a/media/base/mime_util_unittest.cc b/media/base/mime_util_unittest.cc
index a358d0a..e541c9c 100644
--- a/media/base/mime_util_unittest.cc
+++ b/media/base/mime_util_unittest.cc
@@ -172,32 +172,26 @@
       "application/vnd.apple.mpegurl"));
   EXPECT_EQ(kHlsSupported, IsSupportedMediaMimeType("audio/mpegurl"));
   EXPECT_EQ(kHlsSupported, IsSupportedMediaMimeType("audio/x-mpegurl"));
-
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
   EXPECT_TRUE(IsSupportedMediaMimeType("audio/mp4"));
-  EXPECT_TRUE(IsSupportedMediaMimeType("audio/x-m4a"));
-  EXPECT_TRUE(IsSupportedMediaMimeType("video/mp4"));
-  EXPECT_TRUE(IsSupportedMediaMimeType("video/x-m4v"));
-
   EXPECT_TRUE(IsSupportedMediaMimeType("audio/mp3"));
   EXPECT_TRUE(IsSupportedMediaMimeType("audio/x-mp3"));
   EXPECT_TRUE(IsSupportedMediaMimeType("audio/mpeg"));
+  EXPECT_TRUE(IsSupportedMediaMimeType("video/mp4"));
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+  EXPECT_TRUE(IsSupportedMediaMimeType("audio/x-m4a"));
+  EXPECT_TRUE(IsSupportedMediaMimeType("video/x-m4v"));
   EXPECT_TRUE(IsSupportedMediaMimeType("audio/aac"));
 
 #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
   EXPECT_TRUE(IsSupportedMediaMimeType("video/mp2t"));
 #else
   EXPECT_FALSE(IsSupportedMediaMimeType("video/mp2t"));
-#endif
-#else
-  EXPECT_FALSE(IsSupportedMediaMimeType("audio/mp4"));
-  EXPECT_FALSE(IsSupportedMediaMimeType("audio/x-m4a"));
-  EXPECT_FALSE(IsSupportedMediaMimeType("video/mp4"));
-  EXPECT_FALSE(IsSupportedMediaMimeType("video/x-m4v"));
+#endif  // BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
 
-  EXPECT_FALSE(IsSupportedMediaMimeType("audio/mp3"));
-  EXPECT_FALSE(IsSupportedMediaMimeType("audio/x-mp3"));
-  EXPECT_FALSE(IsSupportedMediaMimeType("audio/mpeg"));
+#else
+  EXPECT_FALSE(IsSupportedMediaMimeType("audio/x-m4a"));
+  EXPECT_FALSE(IsSupportedMediaMimeType("video/x-m4v"));
   EXPECT_FALSE(IsSupportedMediaMimeType("audio/aac"));
 #endif  // USE_PROPRIETARY_CODECS
   EXPECT_FALSE(IsSupportedMediaMimeType("video/mp3"));
@@ -328,11 +322,9 @@
     EXPECT_EQ(kCodecAAC, out_codec);
   }
 
-  // Valid FLAC string when proprietary codecs are supported. FLAC-in-MP4
-  // currently requires proprietary codecs to demux mp4.
-  EXPECT_EQ(kUsePropCodecs,
-            ParseAudioCodecString("audio/mp4", "flac", &out_is_ambiguous,
-                                  &out_codec));
+  // Valid FLAC string with MP4. Neither decoding nor demuxing is proprietary.
+  EXPECT_TRUE(ParseAudioCodecString("audio/mp4", "flac", &out_is_ambiguous,
+                                    &out_codec));
   if (kUsePropCodecs) {
     EXPECT_FALSE(out_is_ambiguous);
     EXPECT_EQ(kCodecFLAC, out_codec);
diff --git a/media/base/test_helpers.h b/media/base/test_helpers.h
index 3df27b11..99940f4 100644
--- a/media/base/test_helpers.h
+++ b/media/base/test_helpers.h
@@ -241,6 +241,11 @@
          CONTAINS_STRING(arg, ", which is after the frame's PTS");
 }
 
+MATCHER_P2(CodecUnsupportedInContainer, codec, container, "") {
+  return CONTAINS_STRING(arg, std::string(codec) + "' is not supported for '" +
+                                  std::string(container));
+}
+
 MATCHER_P(FoundStream, stream_type_string, "") {
   return CONTAINS_STRING(
       arg, "found_" + std::string(stream_type_string) + "_stream\":true");
diff --git a/media/cdm/BUILD.gn b/media/cdm/BUILD.gn
index de39dee0..ddb8b07c 100644
--- a/media/cdm/BUILD.gn
+++ b/media/cdm/BUILD.gn
@@ -28,6 +28,8 @@
   sources = [
     "aes_decryptor.cc",
     "aes_decryptor.h",
+    "cenc_utils.cc",
+    "cenc_utils.h",
     "default_cdm_factory.cc",
     "default_cdm_factory.h",
     "json_web_key.cc",
@@ -49,13 +51,6 @@
 
   configs += [ "//media:subcomponent_config" ]
 
-  if (proprietary_codecs) {
-    sources += [
-      "cenc_utils.cc",
-      "cenc_utils.h",
-    ]
-  }
-
   if (enable_library_cdms) {
     deps += [
       ":cdm_api",
diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc
index 067563c1..a4ff4e5 100644
--- a/media/cdm/aes_decryptor.cc
+++ b/media/cdm/aes_decryptor.cc
@@ -24,13 +24,10 @@
 #include "media/base/limits.h"
 #include "media/base/video_decoder_config.h"
 #include "media/base/video_frame.h"
+#include "media/cdm/cenc_utils.h"
 #include "media/cdm/json_web_key.h"
 #include "media/media_features.h"
 
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-#include "media/cdm/cenc_utils.h"
-#endif
-
 namespace media {
 
 namespace {
@@ -319,7 +316,6 @@
       keys.push_back(init_data);
       break;
     case EmeInitDataType::CENC:
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
       // |init_data| is a set of 0 or more concatenated 'pssh' boxes.
       if (!GetKeyIdsForCommonSystemId(init_data, &keys)) {
         promise->reject(CdmPromise::Exception::NOT_SUPPORTED_ERROR, 0,
@@ -327,11 +323,6 @@
         return;
       }
       break;
-#else
-      promise->reject(CdmPromise::Exception::NOT_SUPPORTED_ERROR, 0,
-                      "Initialization data type CENC is not supported.");
-      return;
-#endif
     case EmeInitDataType::KEYIDS: {
       std::string init_data_string(init_data.begin(), init_data.end());
       std::string error_message;
diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc
index d89a432..39b30f7 100644
--- a/media/cdm/aes_decryptor_unittest.cc
+++ b/media/cdm/aes_decryptor_unittest.cc
@@ -591,18 +591,11 @@
       0x00, 0x00, 0x00, 0x00  // datasize
   };
 
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
   EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       CdmSessionType::TEMPORARY_SESSION, EmeInitDataType::CENC,
       std::vector<uint8_t>(init_data, init_data + arraysize(init_data)),
       CreateSessionPromise(RESOLVED));
-#else
-  cdm_->CreateSessionAndGenerateRequest(
-      CdmSessionType::TEMPORARY_SESSION, EmeInitDataType::CENC,
-      std::vector<uint8_t>(init_data, init_data + arraysize(init_data)),
-      CreateSessionPromise(REJECTED));
-#endif
 }
 
 TEST_P(AesDecryptorTest, CreateSessionWithKeyIdsInitData) {
diff --git a/media/cdm/cdm_adapter_unittest.cc b/media/cdm/cdm_adapter_unittest.cc
index 29771df7..c70fc59 100644
--- a/media/cdm/cdm_adapter_unittest.cc
+++ b/media/cdm/cdm_adapter_unittest.cc
@@ -295,11 +295,7 @@
 
   std::vector<uint8_t> key_id(kKeyIdAsPssh,
                               kKeyIdAsPssh + arraysize(kKeyIdAsPssh));
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
   CreateSessionAndExpect(EmeInitDataType::CENC, key_id, SUCCESS);
-#else
-  CreateSessionAndExpect(EmeInitDataType::CENC, key_id, FAILURE);
-#endif
 }
 
 TEST_F(CdmAdapterTest, CreateSessionWithBadData) {
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index e6b14732..01de632 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -3104,13 +3104,17 @@
   ChunkDemuxer::Status expected = ChunkDemuxer::kNotSupported;
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
+
 #if defined(OS_ANDROID)
   if (HasPlatformDecoderSupport())
     expected = ChunkDemuxer::kOk;
 #else
   expected = ChunkDemuxer::kOk;
-#endif
-#endif
+#endif  // defined(OS_ANDROID)
+
+#else
+  EXPECT_MEDIA_LOG(CodecUnsupportedInContainer("avc1.4D4041", "video/mp4"));
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
   EXPECT_EQ(AddId("source_id", "video/mp4", "avc1.4D4041"), expected);
 }
@@ -3120,9 +3124,6 @@
 TEST_P(ChunkDemuxerTest, CodecIDsThatAreNotRFC6381Compliant) {
   ChunkDemuxer::Status expected = ChunkDemuxer::kNotSupported;
 
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-  expected = ChunkDemuxer::kOk;
-#endif
   const char* codec_ids[] = {
     // GPAC places leading zeros on the audio object type.
     "mp4a.40.02",
@@ -3130,6 +3131,12 @@
   };
 
   for (size_t i = 0; i < arraysize(codec_ids); ++i) {
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+    expected = ChunkDemuxer::kOk;
+#else
+    EXPECT_MEDIA_LOG(CodecUnsupportedInContainer(codec_ids[i], "audio/mp4"));
+#endif
+
     ChunkDemuxer::Status result = AddId("source_id", "audio/mp4", codec_ids[i]);
 
     EXPECT_EQ(result, expected)
@@ -4876,11 +4883,7 @@
 }
 
 TEST_P(ChunkDemuxerTest, Mp4Vp9CodecSupport) {
-  ChunkDemuxer::Status expected = ChunkDemuxer::kNotSupported;
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-  expected = ChunkDemuxer::kOk;
-#endif
-
+  ChunkDemuxer::Status expected = ChunkDemuxer::kOk;
   EXPECT_EQ(AddId("source_id", "video/mp4", "vp09.00.10.08"), expected);
 }
 
diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc
index 2effed7..c958632 100644
--- a/media/filters/stream_parser_factory.cc
+++ b/media/filters/stream_parser_factory.cc
@@ -16,6 +16,7 @@
 #include "build/build_config.h"
 #include "media/base/media.h"
 #include "media/base/media_switches.h"
+#include "media/formats/mp4/mp4_stream_parser.h"
 #include "media/formats/mpeg/adts_stream_parser.h"
 #include "media/formats/mpeg/mpeg1_audio_stream_parser.h"
 #include "media/formats/webm/webm_stream_parser.h"
@@ -27,11 +28,10 @@
 #endif
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
+#include "media/formats/mp4/es_descriptor.h"
 #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
 #include "media/formats/mp2t/mp2t_stream_parser.h"
 #endif
-#include "media/formats/mp4/es_descriptor.h"
-#include "media/formats/mp4/mp4_stream_parser.h"
 #endif
 
 namespace media {
@@ -113,16 +113,6 @@
 }
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-bool CheckIfMseFlacInIsobmffEnabled(const std::string& codec_id,
-                                    MediaLog* media_log) {
-  return base::FeatureList::IsEnabled(kMseFlacInIsobmff);
-}
-
-// AAC Object Type IDs that Chrome supports.
-static const int kAACLCObjectType = 2;
-static const int kAACSBRObjectType = 5;
-static const int kAACPSObjectType = 29;
-
 static int GetMP4AudioObjectType(const std::string& codec_id,
                                  MediaLog* media_log) {
   // From RFC 6381 section 3.3 (ISO Base Media File Format Name Space):
@@ -147,6 +137,11 @@
   return -1;
 }
 
+// AAC Object Type IDs that Chrome supports.
+static const int kAACLCObjectType = 2;
+static const int kAACSBRObjectType = 5;
+static const int kAACPSObjectType = 29;
+
 bool ValidateMP4ACodecID(const std::string& codec_id, MediaLog* media_log) {
   int audio_object_type = GetMP4AudioObjectType(codec_id, media_log);
   if (audio_object_type == kAACLCObjectType ||
@@ -165,12 +160,13 @@
     "avc1.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_H264};
 static const CodecInfo kH264AVC3CodecInfo = {
     "avc3.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_H264};
+
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
 static const CodecInfo kHEVCHEV1CodecInfo = {
     "hev1.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_HEVC};
 static const CodecInfo kHEVCHVC1CodecInfo = {
     "hvc1.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_HEVC};
-#endif
+#endif  // BUILDFLAG(ENABLE_HEVC_DEMUXING)
 #if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
 static const CodecInfo kDolbyVisionAVCCodecInfo1 = {
     "dva1.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_DOLBYVISION};
@@ -181,18 +177,13 @@
     "dvh1.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_DOLBYVISION};
 static const CodecInfo kDolbyVisionHEVCCodecInfo2 = {
     "dvhe.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_DOLBYVISION};
-#endif
-#endif
-static const CodecInfo kMPEG4VP09CodecInfo = {
-    "vp09.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_VP9};
+#endif  // BUILDFLAG(ENABLE_HEVC_DEMUXING)
+#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
 static const CodecInfo kMPEG4AACCodecInfo = {"mp4a.40.*", CodecInfo::AUDIO,
                                              &ValidateMP4ACodecID,
                                              CodecInfo::HISTOGRAM_MPEG4AAC};
 static const CodecInfo kMPEG2AACLCCodecInfo = {
     "mp4a.67", CodecInfo::AUDIO, nullptr, CodecInfo::HISTOGRAM_MPEG2AAC};
-static const CodecInfo kMPEG4FLACCodecInfo = {"flac", CodecInfo::AUDIO,
-                                              &CheckIfMseFlacInIsobmffEnabled,
-                                              CodecInfo::HISTOGRAM_FLAC};
 
 #if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
 // The 'ac-3' and 'ec-3' are mime codec ids for AC3 and EAC3 according to
@@ -213,9 +204,34 @@
                                           CodecInfo::HISTOGRAM_EAC3};
 static const CodecInfo kEAC3CodecInfo3 = {"mp4a.A6", CodecInfo::AUDIO, nullptr,
                                           CodecInfo::HISTOGRAM_EAC3};
-#endif
+#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
 
-static const CodecInfo* const kVideoMP4Codecs[] = {&kH264AVC1CodecInfo,
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
+
+static const CodecInfo kMP3CodecInfo = {nullptr, CodecInfo::AUDIO, nullptr,
+                                        CodecInfo::HISTOGRAM_MP3};
+static const CodecInfo* const kAudioMP3Codecs[] = {&kMP3CodecInfo, nullptr};
+
+static StreamParser* BuildMP3Parser(const std::vector<std::string>& codecs,
+                                    MediaLog* media_log) {
+  return new MPEG1AudioStreamParser();
+}
+
+bool CheckIfMseFlacInIsobmffEnabled(const std::string& codec_id,
+                                    MediaLog* media_log) {
+  return base::FeatureList::IsEnabled(kMseFlacInIsobmff);
+}
+
+static const CodecInfo kMPEG4VP09CodecInfo = {
+    "vp09.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_VP9};
+static const CodecInfo kMPEG4FLACCodecInfo = {"flac", CodecInfo::AUDIO,
+                                              &CheckIfMseFlacInIsobmffEnabled,
+                                              CodecInfo::HISTOGRAM_FLAC};
+
+static const CodecInfo* const kVideoMP4Codecs[] = {&kMPEG4FLACCodecInfo,
+                                                   &kMPEG4VP09CodecInfo,
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+                                                   &kH264AVC1CodecInfo,
                                                    &kH264AVC3CodecInfo,
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
                                                    &kHEVCHEV1CodecInfo,
@@ -229,18 +245,19 @@
                                                    &kDolbyVisionHEVCCodecInfo2,
 #endif
 #endif
-                                                   &kMPEG4VP09CodecInfo,
                                                    &kMPEG4AACCodecInfo,
                                                    &kMPEG2AACLCCodecInfo,
-                                                   &kMPEG4FLACCodecInfo,
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 #if BUILDFLAG(ENABLE_AV1_DECODER)
                                                    &kAV1CodecInfo,
 #endif
                                                    nullptr};
 
-static const CodecInfo* const kAudioMP4Codecs[] = {&kMPEG4AACCodecInfo,
+static const CodecInfo* const kAudioMP4Codecs[] = {&kMPEG4FLACCodecInfo,
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+                                                   &kMPEG4AACCodecInfo,
                                                    &kMPEG2AACLCCodecInfo,
-                                                   &kMPEG4FLACCodecInfo,
+
 #if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
                                                    &kAC3CodecInfo1,
                                                    &kAC3CodecInfo2,
@@ -248,7 +265,8 @@
                                                    &kEAC3CodecInfo1,
                                                    &kEAC3CodecInfo2,
                                                    &kEAC3CodecInfo3,
-#endif
+#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
                                                    nullptr};
 
 static StreamParser* BuildMP4Parser(const std::vector<std::string>& codecs,
@@ -266,7 +284,10 @@
 
   for (size_t i = 0; i < codecs.size(); ++i) {
     std::string codec_id = codecs[i];
-    if (base::MatchPattern(codec_id, kMPEG2AACLCCodecInfo.pattern)) {
+    if (base::MatchPattern(codec_id, kMPEG4FLACCodecInfo.pattern)) {
+      has_flac = true;
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+    } else if (base::MatchPattern(codec_id, kMPEG2AACLCCodecInfo.pattern)) {
       audio_object_types.insert(mp4::kISO_13818_7_AAC_LC);
     } else if (base::MatchPattern(codec_id, kMPEG4AACCodecInfo.pattern)) {
       int audio_object_type = GetMP4AudioObjectType(codec_id, media_log);
@@ -279,8 +300,6 @@
         has_sbr = true;
         break;
       }
-    } else if (base::MatchPattern(codec_id, kMPEG4FLACCodecInfo.pattern)) {
-      has_flac = true;
 #if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
     } else if (base::MatchPattern(codec_id, kAC3CodecInfo1.pattern) ||
                base::MatchPattern(codec_id, kAC3CodecInfo2.pattern) ||
@@ -290,23 +309,14 @@
                base::MatchPattern(codec_id, kEAC3CodecInfo2.pattern) ||
                base::MatchPattern(codec_id, kEAC3CodecInfo3.pattern)) {
       audio_object_types.insert(mp4::kEAC3);
-#endif
+#endif  // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
     }
   }
 
   return new mp4::MP4StreamParser(audio_object_types, has_sbr, has_flac);
 }
-
-static const CodecInfo kMP3CodecInfo = {nullptr, CodecInfo::AUDIO, nullptr,
-                                        CodecInfo::HISTOGRAM_MP3};
-
-static const CodecInfo* const kAudioMP3Codecs[] = {&kMP3CodecInfo, nullptr};
-
-static StreamParser* BuildMP3Parser(const std::vector<std::string>& codecs,
-                                    MediaLog* media_log) {
-  return new MPEG1AudioStreamParser();
-}
-
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
 static const CodecInfo kADTSCodecInfo = {nullptr, CodecInfo::AUDIO, nullptr,
                                          CodecInfo::HISTOGRAM_MPEG4AAC};
 static const CodecInfo* const kAudioADTSCodecs[] = {&kADTSCodecInfo, nullptr};
@@ -351,17 +361,18 @@
 
   return new media::mp2t::Mp2tStreamParser(has_sbr);
 }
-#endif
-#endif
+#endif  // ENABLE_MSE_MPEG2TS_STREAM_PARSER
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 static const SupportedTypeInfo kSupportedTypeInfo[] = {
     {"video/webm", &BuildWebMParser, kVideoWebMCodecs},
     {"audio/webm", &BuildWebMParser, kAudioWebMCodecs},
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-    {"audio/aac", &BuildADTSParser, kAudioADTSCodecs},
     {"audio/mpeg", &BuildMP3Parser, kAudioMP3Codecs},
+    // NOTE: Including proprietary MP4 codecs is gated by build flags above.
     {"video/mp4", &BuildMP4Parser, kVideoMP4Codecs},
     {"audio/mp4", &BuildMP4Parser, kAudioMP4Codecs},
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+    {"audio/aac", &BuildADTSParser, kAudioADTSCodecs},
 #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
     {"video/mp2t", &BuildMP2TParser, kVideoMP2TCodecs},
 #endif
diff --git a/media/formats/BUILD.gn b/media/formats/BUILD.gn
index 27254557..c38fa3e 100644
--- a/media/formats/BUILD.gn
+++ b/media/formats/BUILD.gn
@@ -20,6 +20,25 @@
     "ac3/ac3_util.h",
     "common/offset_byte_queue.cc",
     "common/offset_byte_queue.h",
+    "mp4/box_definitions.cc",
+    "mp4/box_definitions.h",
+    "mp4/box_reader.cc",
+    "mp4/box_reader.h",
+    "mp4/es_descriptor.cc",
+    "mp4/es_descriptor.h",
+    "mp4/fourccs.h",
+    "mp4/mp4_stream_parser.cc",
+    "mp4/mp4_stream_parser.h",
+    "mp4/parse_result.h",
+    "mp4/rcheck.h",
+    "mp4/sample_to_group_iterator.cc",
+    "mp4/sample_to_group_iterator.h",
+    "mp4/track_run_iterator.cc",
+    "mp4/track_run_iterator.h",
+    "mpeg/mpeg1_audio_stream_parser.cc",
+    "mpeg/mpeg1_audio_stream_parser.h",
+    "mpeg/mpeg_audio_stream_parser_base.cc",
+    "mpeg/mpeg_audio_stream_parser_base.h",
     "webm/webm_audio_client.cc",
     "webm/webm_audio_client.h",
     "webm/webm_cluster_parser.cc",
@@ -70,29 +89,10 @@
       "mp4/avc.h",
       "mp4/bitstream_converter.cc",
       "mp4/bitstream_converter.h",
-      "mp4/box_definitions.cc",
-      "mp4/box_definitions.h",
-      "mp4/box_reader.cc",
-      "mp4/box_reader.h",
-      "mp4/es_descriptor.cc",
-      "mp4/es_descriptor.h",
-      "mp4/fourccs.h",
-      "mp4/mp4_stream_parser.cc",
-      "mp4/mp4_stream_parser.h",
-      "mp4/parse_result.h",
-      "mp4/rcheck.h",
-      "mp4/sample_to_group_iterator.cc",
-      "mp4/sample_to_group_iterator.h",
-      "mp4/track_run_iterator.cc",
-      "mp4/track_run_iterator.h",
       "mpeg/adts_constants.cc",
       "mpeg/adts_constants.h",
       "mpeg/adts_stream_parser.cc",
       "mpeg/adts_stream_parser.h",
-      "mpeg/mpeg1_audio_stream_parser.cc",
-      "mpeg/mpeg1_audio_stream_parser.h",
-      "mpeg/mpeg_audio_stream_parser_base.cc",
-      "mpeg/mpeg_audio_stream_parser_base.h",
     ]
   }
 
diff --git a/media/formats/mp4/box_definitions.cc b/media/formats/mp4/box_definitions.cc
index ea6926ca..78c6b2d9 100644
--- a/media/formats/mp4/box_definitions.cc
+++ b/media/formats/mp4/box_definitions.cc
@@ -14,20 +14,23 @@
 #include "media/base/media_switches.h"
 #include "media/base/video_types.h"
 #include "media/base/video_util.h"
-#include "media/formats/mp4/avc.h"
 #include "media/formats/mp4/es_descriptor.h"
 #include "media/formats/mp4/rcheck.h"
 #include "media/media_features.h"
-#include "media/video/h264_parser.h"
 #include "third_party/libaom/av1_features.h"
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+#include "media/formats/mp4/avc.h"
+#include "media/video/h264_parser.h"  // nogncheck
+
 #if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
 #include "media/formats/mp4/dolby_vision.h"
-#endif
+#endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
 
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
 #include "media/formats/mp4/hevc.h"
-#endif
+#endif  // BUILDFLAG(ENABLE_HEVC_DEMUXING)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 namespace media {
 namespace mp4 {
@@ -577,6 +580,7 @@
   return true;
 }
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
 AVCDecoderConfigurationRecord::AVCDecoderConfigurationRecord()
     : version(0),
       profile_indication(0),
@@ -636,6 +640,7 @@
 
   return true;
 }
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 VPCodecConfigurationRecord::VPCodecConfigurationRecord()
     : profile(VIDEO_CODEC_PROFILE_UNKNOWN) {}
@@ -731,6 +736,7 @@
   const FourCC actual_format =
       format == FOURCC_ENCV ? sinf.format.format : format;
   switch (actual_format) {
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
     case FOURCC_AVC1:
     case FOURCC_AVC3: {
       DVLOG(2) << __func__ << " reading AVCDecoderConfigurationRecord (avcC)";
@@ -813,6 +819,7 @@
     }
 #endif  // BUILDFLAG(ENABLE_HEVC_DEMUXING)
 #endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
     case FOURCC_VP09: {
       DVLOG(2) << __func__ << " parsing VPCodecConfigurationRecord (vpcC)";
       std::unique_ptr<VPCodecConfigurationRecord> vp_config(
@@ -855,6 +862,7 @@
   const FourCC actual_format =
       format == FOURCC_ENCV ? sinf.format.format : format;
   switch (actual_format) {
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
     case FOURCC_AVC1:
     case FOURCC_AVC3:
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
@@ -869,6 +877,7 @@
     case FOURCC_DVA1:
     case FOURCC_DVAV:
 #endif  // BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
     case FOURCC_VP09:
       return true;
 #if BUILDFLAG(ENABLE_AV1_DECODER)
@@ -902,8 +911,13 @@
 
   object_type = es_desc.object_type();
 
-  if (es_desc.IsAAC(object_type))
+  if (es_desc.IsAAC(object_type)) {
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
     RCHECK(aac.Parse(es_desc.decoder_specific_info(), reader->media_log()));
+#else
+    return false;
+#endif
+  }
 
   return true;
 }
diff --git a/media/formats/mp4/box_definitions.h b/media/formats/mp4/box_definitions.h
index 9a0a711c0..62ec5eb 100644
--- a/media/formats/mp4/box_definitions.h
+++ b/media/formats/mp4/box_definitions.h
@@ -210,6 +210,7 @@
   std::string name;
 };
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
 struct MEDIA_EXPORT AVCDecoderConfigurationRecord : Box {
   DECLARE_BOX_METHODS(AVCDecoderConfigurationRecord);
 
@@ -235,6 +236,7 @@
  private:
   bool ParseInternal(BufferReader* reader, MediaLog* media_log);
 };
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 struct MEDIA_EXPORT VPCodecConfigurationRecord : Box {
   DECLARE_BOX_METHODS(VPCodecConfigurationRecord);
@@ -272,7 +274,9 @@
   DECLARE_BOX_METHODS(ElementaryStreamDescriptor);
 
   uint8_t object_type;
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
   AAC aac;
+#endif
 };
 
 struct MEDIA_EXPORT FlacSpecificBox : Box {
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index d93cebc..cb389ad 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -285,7 +285,6 @@
       if (desc_idx >= samp_descr.audio_entries.size())
         desc_idx = 0;
       const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx];
-      const AAC& aac = entry.esds.aac;
 
       // For encrypted audio streams entry.format is FOURCC_ENCA and actual
       // format is in entry.sinf.format.format.
@@ -324,6 +323,7 @@
         channel_layout = GuessChannelLayout(entry.channelcount);
         sample_per_second = entry.samplerate;
         extra_data = entry.dfla.stream_info;
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
       } else {
         uint8_t audio_type = entry.esds.object_type;
 #if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
@@ -346,6 +346,7 @@
         // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or
         // supported MPEG2 AAC varients.
         if (ESDescriptor::IsAAC(audio_type)) {
+          const AAC& aac = entry.esds.aac;
           codec = kCodecAAC;
           channel_layout = aac.GetChannelLayout(has_sbr_);
           sample_per_second = aac.GetOutputSamplesPerSecond(has_sbr_);
@@ -368,6 +369,7 @@
               << static_cast<int>(audio_type) << " in esds.";
           return false;
         }
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
       }
 
       SampleFormat sample_format;
@@ -596,6 +598,7 @@
   encrypted_media_init_data_cb_.Run(EmeInitDataType::CENC, init_data);
 }
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
 bool MP4StreamParser::PrepareAACBuffer(
     const AAC& aac_config,
     std::vector<uint8_t>* frame_buf,
@@ -613,6 +616,7 @@
   }
   return true;
 }
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 ParseResult MP4StreamParser::EnqueueSample(BufferQueueMap* buffers) {
   DCHECK_EQ(state_, kEmittingSamples);
@@ -732,11 +736,17 @@
   }
 
   if (audio) {
-    if (ESDescriptor::IsAAC(runs_->audio_description().esds.object_type) &&
-        !PrepareAACBuffer(runs_->audio_description().esds.aac,
-                          &frame_buf, &subsamples)) {
-      MEDIA_LOG(ERROR, media_log_) << "Failed to prepare AAC sample for decode";
+    if (ESDescriptor::IsAAC(runs_->audio_description().esds.object_type)) {
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+      if (!PrepareAACBuffer(runs_->audio_description().esds.aac, &frame_buf,
+                            &subsamples)) {
+        MEDIA_LOG(ERROR, media_log_)
+            << "Failed to prepare AAC sample for decode";
+        return ParseResult::kError;
+      }
+#else
       return ParseResult::kError;
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
     }
   }
 
diff --git a/media/formats/mp4/mp4_stream_parser.h b/media/formats/mp4/mp4_stream_parser.h
index ff1d84a..97bdfd6 100644
--- a/media/formats/mp4/mp4_stream_parser.h
+++ b/media/formats/mp4/mp4_stream_parser.h
@@ -20,6 +20,10 @@
 #include "media/formats/mp4/parse_result.h"
 #include "media/formats/mp4/track_run_iterator.h"
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+#include "media/formats/mp4/aac.h"
+#endif
+
 namespace media {
 namespace mp4 {
 
@@ -73,9 +77,11 @@
   void ChangeState(State new_state);
 
   bool EmitConfigs();
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
   bool PrepareAACBuffer(const AAC& aac_config,
                         std::vector<uint8_t>* frame_buf,
                         std::vector<SubsampleEntry>* subsamples) const;
+#endif
   ParseResult EnqueueSample(BufferQueueMap* buffers);
   bool SendAndFlushSamples(BufferQueueMap* buffers);
 
diff --git a/media/mojo/common/BUILD.gn b/media/mojo/common/BUILD.gn
index 637252e..72d6a25d 100644
--- a/media/mojo/common/BUILD.gn
+++ b/media/mojo/common/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "media_type_converters.cc",
     "media_type_converters.h",
+    "mojo_data_pipe_read_write.cc",
+    "mojo_data_pipe_read_write.h",
     "mojo_decoder_buffer_converter.cc",
     "mojo_decoder_buffer_converter.h",
   ]
@@ -45,6 +47,7 @@
 
   sources = [
     "media_type_converters_unittest.cc",
+    "mojo_data_pipe_read_write_unittest.cc",
     "mojo_decoder_buffer_converter_unittest.cc",
     "mojo_shared_buffer_video_frame_unittest.cc",
   ]
diff --git a/media/mojo/common/mojo_data_pipe_read_write.cc b/media/mojo/common/mojo_data_pipe_read_write.cc
new file mode 100644
index 0000000..067e681
--- /dev/null
+++ b/media/mojo/common/mojo_data_pipe_read_write.cc
@@ -0,0 +1,247 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/mojo/common/mojo_data_pipe_read_write.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+
+namespace media {
+
+namespace {
+
+bool IsPipeReadWriteError(MojoResult result) {
+  return result != MOJO_RESULT_OK && result != MOJO_RESULT_SHOULD_WAIT;
+}
+
+}  // namespace
+
+// MojoDataPipeReader
+
+MojoDataPipeReader::MojoDataPipeReader(
+    mojo::ScopedDataPipeConsumerHandle consumer_handle)
+    : consumer_handle_(std::move(consumer_handle)),
+      pipe_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL) {
+  DVLOG(1) << __func__;
+
+  MojoResult result = pipe_watcher_.Watch(
+      consumer_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
+      MOJO_WATCH_CONDITION_SATISFIED,
+      base::BindRepeating(&MojoDataPipeReader::OnPipeReadable,
+                          base::Unretained(this)));
+  if (result != MOJO_RESULT_OK) {
+    DVLOG(1) << __func__
+             << ": Failed to start watching the pipe. result=" << result;
+    consumer_handle_.reset();
+  }
+}
+
+MojoDataPipeReader::~MojoDataPipeReader() {
+  DVLOG(1) << __func__;
+}
+
+void MojoDataPipeReader::CompleteCurrentRead() {
+  DVLOG(4) << __func__;
+  DCHECK(!done_cb_.is_null());
+  current_buffer_size_ = 0;
+  std::move(done_cb_).Run(true);
+}
+
+void MojoDataPipeReader::Read(uint8_t* buffer,
+                              uint32_t num_bytes,
+                              DoneCB done_cb) {
+  DVLOG(3) << __func__;
+  // Read() can not be called when there is another reading request in process.
+  DCHECK(!current_buffer_size_);
+  DCHECK(!done_cb.is_null());
+  if (!num_bytes) {
+    std::move(done_cb).Run(true);
+    return;
+  }
+
+  if (!consumer_handle_.is_valid()) {
+    VLOG(1) << __func__ << ": Data pipe was closed.";
+    std::move(done_cb).Run(false);
+    return;
+  }
+
+  current_buffer_size_ = num_bytes;
+  current_buffer_ = buffer;
+  bytes_read_ = 0;
+  done_cb_ = std::move(done_cb);
+
+  pipe_watcher_.ArmOrNotify();
+}
+
+void MojoDataPipeReader::OnPipeReadable(MojoResult result,
+                                        const mojo::HandleSignalsState& state) {
+  DVLOG(4) << __func__ << "(" << result << ", " << state.readable() << ")";
+
+  if (result != MOJO_RESULT_OK) {
+    OnPipeError(result);
+    return;
+  }
+
+  DCHECK(state.readable());
+  DCHECK_GT(current_buffer_size_, bytes_read_);
+  uint32_t num_bytes = current_buffer_size_ - bytes_read_;
+  if (current_buffer_) {
+    result = consumer_handle_->ReadData(current_buffer_ + bytes_read_,
+                                        &num_bytes, MOJO_READ_DATA_FLAG_NONE);
+  } else {
+    result = consumer_handle_->ReadData(nullptr, &num_bytes,
+                                        MOJO_READ_DATA_FLAG_DISCARD);
+  }
+
+  if (IsPipeReadWriteError(result)) {
+    OnPipeError(result);
+  } else {
+    if (result == MOJO_RESULT_OK) {
+      DCHECK_GT(num_bytes, 0u);
+      bytes_read_ += num_bytes;
+      if (bytes_read_ == current_buffer_size_) {
+        CompleteCurrentRead();
+        return;
+      }
+    }
+    pipe_watcher_.ArmOrNotify();
+  }
+}
+
+void MojoDataPipeReader::OnPipeError(MojoResult result) {
+  DVLOG(1) << __func__ << "(" << result << ")";
+  DCHECK(IsPipeReadWriteError(result));
+
+  consumer_handle_.reset();
+
+  if (current_buffer_) {
+    DVLOG(1) << __func__ << ": reading from data pipe failed. result=" << result
+             << ", buffer size=" << current_buffer_size_
+             << ", num_bytes(read)=" << bytes_read_;
+    bytes_read_ = 0;
+    current_buffer_ = nullptr;
+    current_buffer_size_ = 0;
+    DCHECK(!done_cb_.is_null());
+    std::move(done_cb_).Run(false);
+  }
+}
+
+bool MojoDataPipeReader::IsPipeValid() const {
+  return consumer_handle_.is_valid();
+}
+
+// MojoDataPipeWriter
+
+MojoDataPipeWriter::MojoDataPipeWriter(
+    mojo::ScopedDataPipeProducerHandle producer_handle)
+    : producer_handle_(std::move(producer_handle)),
+      pipe_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL) {
+  DVLOG(1) << __func__;
+
+  MojoResult result = pipe_watcher_.Watch(
+      producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+      MOJO_WATCH_CONDITION_SATISFIED,
+      base::BindRepeating(&MojoDataPipeWriter::OnPipeWritable,
+                          base::Unretained(this)));
+  if (result != MOJO_RESULT_OK) {
+    DVLOG(1) << __func__
+             << ": Failed to start watching the pipe. result=" << result;
+    producer_handle_.reset();
+  }
+}
+
+MojoDataPipeWriter::~MojoDataPipeWriter() {
+  DVLOG(1) << __func__;
+}
+
+void MojoDataPipeWriter::Write(const uint8_t* buffer,
+                               uint32_t buffer_size,
+                               DoneCB done_cb) {
+  DVLOG(3) << __func__;
+  // Write() can not be called when another writing request is in process.
+  DCHECK(!current_buffer_);
+  DCHECK(!done_cb.is_null());
+  if (!buffer_size) {
+    std::move(done_cb).Run(true);
+    return;
+  }
+  DCHECK(buffer);
+
+  // Cannot write if the pipe is already closed.
+  if (!producer_handle_.is_valid()) {
+    DVLOG(1) << __func__
+             << ": Failed to write buffer becuase the pipe is already closed";
+    std::move(done_cb).Run(false);
+    return;
+  }
+
+  current_buffer_ = buffer;
+  current_buffer_size_ = buffer_size;
+  bytes_written_ = 0;
+  done_cb_ = std::move(done_cb);
+  pipe_watcher_.ArmOrNotify();
+}
+
+void MojoDataPipeWriter::OnPipeWritable(MojoResult result,
+                                        const mojo::HandleSignalsState& state) {
+  if (result != MOJO_RESULT_OK) {
+    OnPipeError(result);
+    return;
+  }
+
+  DCHECK(state.writable());
+  DCHECK(current_buffer_);
+  DCHECK_GT(current_buffer_size_, bytes_written_);
+  uint32_t num_bytes = current_buffer_size_ - bytes_written_;
+
+  result = producer_handle_->WriteData(current_buffer_ + bytes_written_,
+                                       &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+
+  if (IsPipeReadWriteError(result)) {
+    OnPipeError(result);
+  } else {
+    if (result == MOJO_RESULT_OK) {
+      DCHECK_GT(num_bytes, 0u);
+      bytes_written_ += num_bytes;
+      if (bytes_written_ == current_buffer_size_) {
+        CompleteCurrentWrite();
+        return;
+      }
+    }
+    pipe_watcher_.ArmOrNotify();
+  }
+}
+
+void MojoDataPipeWriter::CompleteCurrentWrite() {
+  DVLOG(4) << __func__;
+  DCHECK(!done_cb_.is_null());
+  current_buffer_ = nullptr;
+  std::move(done_cb_).Run(true);
+}
+
+void MojoDataPipeWriter::OnPipeError(MojoResult result) {
+  DVLOG(1) << __func__ << "(" << result << ")";
+  DCHECK(IsPipeReadWriteError(result));
+
+  producer_handle_.reset();
+
+  if (current_buffer_) {
+    DVLOG(1) << __func__ << ": writing to data pipe failed. result=" << result
+             << ", buffer size=" << current_buffer_size_
+             << ", num_bytes(written)=" << bytes_written_;
+    current_buffer_ = nullptr;
+    current_buffer_size_ = 0;
+    bytes_written_ = 0;
+    DCHECK(!done_cb_.is_null());
+    std::move(done_cb_).Run(false);
+  }
+}
+
+bool MojoDataPipeWriter::IsPipeValid() const {
+  return producer_handle_.is_valid();
+}
+
+}  // namespace media
diff --git a/media/mojo/common/mojo_data_pipe_read_write.h b/media/mojo/common/mojo_data_pipe_read_write.h
new file mode 100644
index 0000000..bfacacf
--- /dev/null
+++ b/media/mojo/common/mojo_data_pipe_read_write.h
@@ -0,0 +1,114 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_MOJO_COMMON_MOJO_DATA_PIPE_READ_WRITE_H_
+#define MEDIA_MOJO_COMMON_MOJO_DATA_PIPE_READ_WRITE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+
+namespace media {
+
+// An async reader to read a certain amount of bytes from a mojo data pipe by
+// request.
+class MojoDataPipeReader {
+ public:
+  explicit MojoDataPipeReader(
+      mojo::ScopedDataPipeConsumerHandle consumer_handle);
+
+  ~MojoDataPipeReader();
+
+  using DoneCB = base::OnceCallback<void(bool)>;
+  // Reads |num_bytes| data from the mojo data pipe into |buffer|. When the
+  // operation completes, |done_cb| is called and indicates whether the reading
+  // succeeded. This is not allowed to be called when another reading is
+  // ongoing. When |buffer| is null, the data will be discarded. Otherwise,
+  // |buffer| needs to be valid for writing during the entire reading process.
+  // |done_cb| will be called immediately if |num_bytes| is zero or the data
+  // pipe is closed without doing anything.
+  void Read(uint8_t* buffer, uint32_t num_bytes, DoneCB done_cb);
+
+  bool IsPipeValid() const;
+
+ private:
+  void CompleteCurrentRead();
+  void OnPipeReadable(MojoResult result, const mojo::HandleSignalsState& state);
+  void OnPipeError(MojoResult result);
+
+  // Read side of the data pipe.
+  mojo::ScopedDataPipeConsumerHandle consumer_handle_;
+
+  // Provides notification about |consumer_handle_| readiness.
+  mojo::SimpleWatcher pipe_watcher_;
+
+  // The current buffer to be read. It is provided by Read() and should be
+  // guaranteed to be valid until the current read completes.
+  uint8_t* current_buffer_ = nullptr;
+
+  // The number of bytes to be read for the current read request.
+  uint32_t current_buffer_size_ = 0;
+
+  // The current once callback to be called when read completes.
+  DoneCB done_cb_;
+
+  // Number of bytes already read into the current buffer.
+  uint32_t bytes_read_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoDataPipeReader);
+};
+
+// An async writer to write a certain amount of data into a mojo data pipe by
+// request.
+class MojoDataPipeWriter {
+ public:
+  explicit MojoDataPipeWriter(
+      mojo::ScopedDataPipeProducerHandle producer_handle);
+
+  ~MojoDataPipeWriter();
+
+  using DoneCB = base::OnceCallback<void(bool)>;
+  // Writes |num_bytes| data from |buffer| into the mojo data pipe. When the
+  // operation completes, |done_cb| is called and indicates whether the writing
+  // succeeded. This is not allowed to be called when another writing is
+  // ongoing. |buffer| needs to be valid for reading during the entire writing
+  // process. |done_cb| will be called immediately if |num_bytes| is zero or
+  // the data pipe is closed without doing anything.
+  void Write(const uint8_t* buffer, uint32_t num_bytes, DoneCB done_cb);
+
+  bool IsPipeValid() const;
+
+ private:
+  void OnPipeWritable(MojoResult result, const mojo::HandleSignalsState& state);
+  void OnPipeError(MojoResult result);
+  void CompleteCurrentWrite();
+
+  // Write side of the data pipe.
+  mojo::ScopedDataPipeProducerHandle producer_handle_;
+
+  // Provides notifications about |producer_handle_| readiness.
+  mojo::SimpleWatcher pipe_watcher_;
+
+  // The current buffer to be written. It is provided by Write() and should be
+  // guaranteed to be valid until the current write completes.
+  const uint8_t* current_buffer_ = nullptr;
+
+  // The number of bytes to be written for the current write request.
+  uint32_t current_buffer_size_ = 0;
+
+  // The current once callback to be called when write completes.
+  DoneCB done_cb_;
+
+  // Number of bytes already written from the current buffer.
+  uint32_t bytes_written_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoDataPipeWriter);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_MOJO_COMMON_MOJO_DATA_PIPE_READ_WRITE_H_
diff --git a/media/mojo/common/mojo_data_pipe_read_write_unittest.cc b/media/mojo/common/mojo_data_pipe_read_write_unittest.cc
new file mode 100644
index 0000000..e4a2487
--- /dev/null
+++ b/media/mojo/common/mojo_data_pipe_read_write_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/mojo/common/mojo_data_pipe_read_write.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "media/base/decoder_buffer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+uint32_t kDefaultDataPipeCapacityBytes = 512;
+
+class MojoDataPipeReadWrite {
+ public:
+  MojoDataPipeReadWrite(
+      uint32_t data_pipe_capacity_bytes = kDefaultDataPipeCapacityBytes) {
+    mojo::DataPipe data_pipe(data_pipe_capacity_bytes);
+
+    writer_ = base::MakeUnique<MojoDataPipeWriter>(
+        std::move(data_pipe.producer_handle));
+    reader_ = base::MakeUnique<MojoDataPipeReader>(
+        std::move(data_pipe.consumer_handle));
+  }
+
+  void WriteAndRead(const uint8_t* buffer,
+                    uint32_t buffer_size,
+                    bool discard_data = false) {
+    base::RunLoop run_loop;
+    base::MockCallback<MojoDataPipeWriter::DoneCB> mock_write_cb;
+    base::MockCallback<MojoDataPipeReader::DoneCB> mock_read_cb;
+    EXPECT_TRUE(reader_->IsPipeValid());
+    EXPECT_TRUE(writer_->IsPipeValid());
+    EXPECT_CALL(mock_write_cb, Run(true)).Times(1);
+    EXPECT_CALL(mock_read_cb, Run(true)).Times(1);
+
+    writer_->Write(buffer, buffer_size, mock_write_cb.Get());
+    EXPECT_TRUE(read_buffer_.empty());
+    if (discard_data) {
+      reader_->Read(nullptr, buffer_size, mock_read_cb.Get());
+      run_loop.RunUntilIdle();
+    } else {
+      read_buffer_.resize(buffer_size);
+      reader_->Read(read_buffer_.data(), buffer_size, mock_read_cb.Get());
+      run_loop.RunUntilIdle();
+      EXPECT_EQ(0, std::memcmp(buffer, read_buffer_.data(), buffer_size));
+      read_buffer_.clear();
+    }
+  }
+
+  std::unique_ptr<MojoDataPipeWriter> writer_;
+  std::unique_ptr<MojoDataPipeReader> reader_;
+  std::vector<uint8_t> read_buffer_;
+};
+
+}  // namespace
+
+TEST(MojoDataPipeReadWriteTest, Normal) {
+  base::MessageLoop message_loop;
+  std::string kData = "hello, world";
+  MojoDataPipeReadWrite pipe_read_write_;
+  pipe_read_write_.WriteAndRead(reinterpret_cast<const uint8_t*>(kData.data()),
+                                kData.size());
+}
+
+TEST(MojoDataPipeReadWriteTest, SequentialReading) {
+  base::MessageLoop message_loop;
+  std::string kData1 = "hello, world";
+  std::string kData2 = "Bye!";
+  MojoDataPipeReadWrite pipe_read_write_;
+  pipe_read_write_.WriteAndRead(reinterpret_cast<const uint8_t*>(kData1.data()),
+                                kData1.size());
+  pipe_read_write_.WriteAndRead(reinterpret_cast<const uint8_t*>(kData2.data()),
+                                kData2.size());
+}
+
+TEST(MojoDataPipeReadWriteTest, LongerThanCapacity) {
+  base::MessageLoop message_loop;
+  std::string kData = "hello, world, hello, world, hello, world";
+  MojoDataPipeReadWrite pipe_read_write_(10);
+  pipe_read_write_.WriteAndRead(reinterpret_cast<const uint8_t*>(kData.data()),
+                                kData.size());
+}
+
+TEST(MojoDataPipeReadWriteTest, DiscardDataInPipe) {
+  base::MessageLoop message_loop;
+  std::string kData1 = "to be discarded";
+  std::string kData2 = "hello, world, hello, world, hello, world";
+  MojoDataPipeReadWrite pipe_read_write_(10);
+  pipe_read_write_.WriteAndRead(reinterpret_cast<const uint8_t*>(kData1.data()),
+                                kData1.size(), true);
+  pipe_read_write_.WriteAndRead(reinterpret_cast<const uint8_t*>(kData2.data()),
+                                kData2.size());
+}
+
+}  // namespace media
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index ec89f78..9f7d1846 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -114,24 +114,24 @@
 const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\"";
 const char kOpusAudioOnlyWebM[] = "video/webm; codecs=\"opus\"";
 const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\"";
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-const char kADTS[] = "audio/aac";
-const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\"";
-const char kMP4VideoAVC3[] = "video/mp4; codecs=\"avc3.64001f\"";
 const char kMP4VideoVP9[] =
     "video/mp4; codecs=\"vp09.00.10.08.01.02.02.02.00\"";
-const char kMP4VideoHEVC1[] = "video/mp4; codecs=\"hvc1.1.6.L93.B0\"";
-const char kMP4VideoHEVC2[] = "video/mp4; codecs=\"hev1.1.6.L93.B0\"";
-const char kMP4Video[] = "video/mp4; codecs=\"avc1.4D4041\"";
-const char kMP4Audio[] = "audio/mp4; codecs=\"mp4a.40.2\"";
 const char kMP4AudioFlac[] = "audio/mp4; codecs=\"flac\"";
 const char kMP3[] = "audio/mpeg";
-const char kMP2AudioSBR[] = "video/mp2t; codecs=\"avc1.4D4041,mp4a.40.5\"";
 #if BUILDFLAG(ENABLE_AV1_DECODER)
 // TODO(dalecurtis): This is not the correct final string. Fix before enabling
 // by default. http://crbug.com/784607
 const char kMP4AV1[] = "video/mp4; codecs=\"av1\"";
 #endif  // BUILDFLAG(ENABLE_AV1_DECODER)
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+const char kADTS[] = "audio/aac";
+const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\"";
+const char kMP4VideoAVC3[] = "video/mp4; codecs=\"avc3.64001f\"";
+const char kMP4VideoHEVC1[] = "video/mp4; codecs=\"hvc1.1.6.L93.B0\"";
+const char kMP4VideoHEVC2[] = "video/mp4; codecs=\"hev1.1.6.L93.B0\"";
+const char kMP4Video[] = "video/mp4; codecs=\"avc1.4D4041\"";
+const char kMP4Audio[] = "audio/mp4; codecs=\"mp4a.40.2\"";
+const char kMP2AudioSBR[] = "video/mp2t; codecs=\"avc1.4D4041,mp4a.40.5\"";
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 // Constants for the Media Source config change tests.
@@ -1537,31 +1537,6 @@
 }
 #endif
 
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-
-TEST_F(PipelineIntegrationTest, BasicPlaybackHi10P) {
-  ASSERT_EQ(PIPELINE_OK, Start("bear-320x180-hi10p.mp4"));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-}
-
-std::vector<std::unique_ptr<VideoDecoder>> CreateFailingVideoDecoder() {
-  std::vector<std::unique_ptr<VideoDecoder>> failing_video_decoder;
-  failing_video_decoder.push_back(base::MakeUnique<FailingVideoDecoder>());
-  return failing_video_decoder;
-}
-
-TEST_F(PipelineIntegrationTest, BasicFallback) {
-  ASSERT_EQ(PIPELINE_OK,
-            Start("bear.mp4", kNormal, base::Bind(&CreateFailingVideoDecoder)));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-};
-
 #if BUILDFLAG(ENABLE_AV1_DECODER)
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_AV1_MP4) {
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -1603,55 +1578,6 @@
   EXPECT_HASH_EQ(kSfxLosslessHash, GetAudioHash());
 }
 
-TEST_P(MSEPipelineIntegrationTest, ADTS) {
-  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK,
-            StartPipelineWithMediaSource(&source, kHashed, nullptr));
-  source.EndOfStream();
-
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(325, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  Play();
-
-  EXPECT_TRUE(WaitUntilOnEnded());
-
-  // Verify that nothing was stripped.
-  EXPECT_HASH_EQ("0.46,1.72,4.26,4.57,3.39,1.53,", GetAudioHash());
-}
-
-TEST_P(MSEPipelineIntegrationTest, ADTS_TimestampOffset) {
-  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK,
-            StartPipelineWithMediaSource(&source, kHashed, nullptr));
-  EXPECT_EQ(325, source.last_timestamp_offset().InMilliseconds());
-
-  // Trim off multiple frames off the beginning of the segment which will cause
-  // the first decoded frame to be incorrect if preroll isn't implemented.
-  const base::TimeDelta adts_preroll_duration =
-      base::TimeDelta::FromSecondsD(2.5 * 1024 / 44100);
-  const base::TimeDelta append_time =
-      source.last_timestamp_offset() - adts_preroll_duration;
-
-  scoped_refptr<DecoderBuffer> second_file = ReadTestDataFile("sfx.adts");
-  source.AppendAtTimeWithWindow(
-      append_time, append_time + adts_preroll_duration, kInfiniteDuration,
-      second_file->data(), second_file->data_size());
-  source.EndOfStream();
-
-  Play();
-  EXPECT_TRUE(WaitUntilOnEnded());
-
-  EXPECT_EQ(592, source.last_timestamp_offset().InMilliseconds());
-  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
-  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
-  EXPECT_EQ(592, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
-
-  // Verify preroll is stripped.
-  EXPECT_HASH_EQ("-1.76,-1.35,-0.72,0.70,1.24,0.52,", GetAudioHash());
-}
-
 TEST_F(PipelineIntegrationTest, BasicPlaybackHashed_MP3) {
   ASSERT_EQ(PIPELINE_OK, Start("sfx.mp3", kHashed));
 
@@ -1663,17 +1589,6 @@
   EXPECT_HASH_EQ("1.30,2.72,4.56,5.08,3.74,2.03,", GetAudioHash());
 }
 
-TEST_F(PipelineIntegrationTest, BasicPlaybackHashed_ADTS) {
-  ASSERT_EQ(PIPELINE_OK, Start("sfx.adts", kHashed));
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-
-  // Verify codec delay and preroll are stripped.
-  EXPECT_HASH_EQ("1.80,1.66,2.31,3.26,4.46,3.36,", GetAudioHash());
-}
-
 TEST_F(PipelineIntegrationTest, BasicPlaybackHashed_FlacInMp4) {
   ASSERT_EQ(PIPELINE_OK, Start("sfx-flac.mp4", kHashed));
   Play();
@@ -1814,6 +1729,91 @@
   EXPECT_TRUE(WaitUntilOnEnded());
 }
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+
+TEST_P(MSEPipelineIntegrationTest, ADTS) {
+  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK,
+            StartPipelineWithMediaSource(&source, kHashed, nullptr));
+  source.EndOfStream();
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(325, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  Play();
+
+  EXPECT_TRUE(WaitUntilOnEnded());
+
+  // Verify that nothing was stripped.
+  EXPECT_HASH_EQ("0.46,1.72,4.26,4.57,3.39,1.53,", GetAudioHash());
+}
+
+TEST_P(MSEPipelineIntegrationTest, ADTS_TimestampOffset) {
+  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK,
+            StartPipelineWithMediaSource(&source, kHashed, nullptr));
+  EXPECT_EQ(325, source.last_timestamp_offset().InMilliseconds());
+
+  // Trim off multiple frames off the beginning of the segment which will cause
+  // the first decoded frame to be incorrect if preroll isn't implemented.
+  const base::TimeDelta adts_preroll_duration =
+      base::TimeDelta::FromSecondsD(2.5 * 1024 / 44100);
+  const base::TimeDelta append_time =
+      source.last_timestamp_offset() - adts_preroll_duration;
+
+  scoped_refptr<DecoderBuffer> second_file = ReadTestDataFile("sfx.adts");
+  source.AppendAtTimeWithWindow(
+      append_time, append_time + adts_preroll_duration, kInfiniteDuration,
+      second_file->data(), second_file->data_size());
+  source.EndOfStream();
+
+  Play();
+  EXPECT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_EQ(592, source.last_timestamp_offset().InMilliseconds());
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(592, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  // Verify preroll is stripped.
+  EXPECT_HASH_EQ("-1.76,-1.35,-0.72,0.70,1.24,0.52,", GetAudioHash());
+}
+
+TEST_F(PipelineIntegrationTest, BasicPlaybackHashed_ADTS) {
+  ASSERT_EQ(PIPELINE_OK, Start("sfx.adts", kHashed));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+
+  // Verify codec delay and preroll are stripped.
+  EXPECT_HASH_EQ("1.80,1.66,2.31,3.26,4.46,3.36,", GetAudioHash());
+}
+
+TEST_F(PipelineIntegrationTest, BasicPlaybackHi10P) {
+  ASSERT_EQ(PIPELINE_OK, Start("bear-320x180-hi10p.mp4"));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+std::vector<std::unique_ptr<VideoDecoder>> CreateFailingVideoDecoder() {
+  std::vector<std::unique_ptr<VideoDecoder>> failing_video_decoder;
+  failing_video_decoder.push_back(base::MakeUnique<FailingVideoDecoder>());
+  return failing_video_decoder;
+}
+
+TEST_F(PipelineIntegrationTest, BasicFallback) {
+  ASSERT_EQ(PIPELINE_OK,
+            Start("bear.mp4", kNormal, base::Bind(&CreateFailingVideoDecoder)));
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+};
+
 TEST_P(MSEPipelineIntegrationTest, ConfigChange_MP4) {
   MockMediaSource source("bear-640x360-av_frag.mp4", kMP4, kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
@@ -2051,6 +2051,37 @@
   Stop();
 }
 
+MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
+                 MAYBE_EME(EncryptedPlayback_MP4_VP9_CENC_VideoOnly)) {
+  MockMediaSource source("bear-320x240-v_frag-vp9-cenc.mp4", kMP4VideoVP9,
+                         kAppendWholeFile);
+  FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
+  EXPECT_EQ(PIPELINE_OK,
+            StartPipelineWithEncryptedMedia(&source, &encrypted_media));
+
+  source.EndOfStream();
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+}
+
+TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_VP9) {
+  MockMediaSource source("bear-320x240-v_frag-vp9.mp4", kMP4VideoVP9,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+  source.EndOfStream();
+  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
+
+  Play();
+
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Shutdown();
+  Stop();
+}
+
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_MP4_CENC_VideoOnly)) {
@@ -2246,23 +2277,6 @@
   Stop();
 }
 
-MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
-                 MAYBE_EME(EncryptedPlayback_MP4_VP9_CENC_VideoOnly)) {
-  MockMediaSource source("bear-320x240-v_frag-vp9-cenc.mp4", kMP4VideoVP9,
-                         kAppendWholeFile);
-  FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
-  EXPECT_EQ(PIPELINE_OK,
-            StartPipelineWithEncryptedMedia(&source, &encrypted_media));
-
-  source.EndOfStream();
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_AVC3) {
   MockMediaSource source("bear-1280x720-v_frag-avc3.mp4", kMP4VideoAVC3,
                          kAppendWholeFile);
@@ -2281,20 +2295,6 @@
   Stop();
 }
 
-TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_VP9) {
-  MockMediaSource source("bear-320x240-v_frag-vp9.mp4", kMP4VideoVP9,
-                         kAppendWholeFile);
-  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
-  source.EndOfStream();
-  ASSERT_EQ(PIPELINE_OK, pipeline_status_);
-
-  Play();
-
-  ASSERT_TRUE(WaitUntilOnEnded());
-  source.Shutdown();
-  Stop();
-}
-
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_HEVC1) {
   // HEVC demuxing might be enabled even on platforms that don't support HEVC
   // decoding. For those cases we'll get DECODER_ERROR_NOT_SUPPORTED, which
diff --git a/remoting/client/gesture_interpreter.cc b/remoting/client/gesture_interpreter.cc
index 638d2c2..32365b5 100644
--- a/remoting/client/gesture_interpreter.cc
+++ b/remoting/client/gesture_interpreter.cc
@@ -105,14 +105,25 @@
   }
   ViewMatrix::Point cursor_position = input_strategy_->GetCursorPosition();
 
-  if (state == GESTURE_BEGAN) {
-    StartInputFeedback(cursor_position.x, cursor_position.y,
-                       TouchInputStrategy::DRAG_FEEDBACK);
+  switch (state) {
+    case GESTURE_BEGAN:
+      StartInputFeedback(cursor_position.x, cursor_position.y,
+                         TouchInputStrategy::DRAG_FEEDBACK);
+      input_stub_->SendMouseEvent(cursor_position.x, cursor_position.y,
+                                  protocol::MouseEvent_MouseButton_BUTTON_LEFT,
+                                  true);
+      break;
+    case GESTURE_CHANGED:
+      InjectCursorPosition(cursor_position.x, cursor_position.y);
+      break;
+    case GESTURE_ENDED:
+      input_stub_->SendMouseEvent(cursor_position.x, cursor_position.y,
+                                  protocol::MouseEvent_MouseButton_BUTTON_LEFT,
+                                  false);
+      break;
+    default:
+      NOTREACHED();
   }
-
-  input_stub_->SendMouseEvent(cursor_position.x, cursor_position.y,
-                              protocol::MouseEvent_MouseButton_BUTTON_LEFT,
-                              is_dragging_mode);
 }
 
 void GestureInterpreter::OneFingerFling(float velocity_x, float velocity_y) {
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 77164e3b..52bff4d 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -1,4 +1,6 @@
 {
+  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
   "CFI Linux (icall)": {
     "gtest_tests": [
       {
@@ -7025,8 +7027,7 @@
           "--gtest_filter=-SaveType/SavePageMultiFrameBrowserTest.ObjectElements/0"
         ],
         "swarming": {
-          "can_use_on_swarming_builders": false,
-          "shards": 10
+          "can_use_on_swarming_builders": false
         },
         "test": "browser_tests"
       },
@@ -7909,11 +7910,14 @@
         }
       },
       {
+        "args": [
+          "--jobs=1"
+        ],
         "isolate_name": "telemetry_unittests",
         "name": "telemetry_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "shards": 2
+          "shards": 4
         }
       }
     ]
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index f70b30ad..c7d55d7 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -295,6 +295,9 @@
     if 'shards' in swarming_dict:
       if swarming_dict['shards'] == 1: # pragma: no cover
         del swarming_dict['shards'] # pragma: no cover
+    if 'hard_timeout' in swarming_dict:
+      if swarming_dict['hard_timeout'] == 0: # pragma: no cover
+        del swarming_dict['hard_timeout'] # pragma: no cover
     if not swarming_dict['can_use_on_swarming_builders']:
       # Remove all other keys.
       for k in swarming_dict.keys(): # pragma: no cover
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index eeab245..848bdb7 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -23,6 +23,8 @@
       'KitKat Phone Tester (rel)',
       'KitKat Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroid x64',
     ],
     'modifications': {
       'Lollipop Tablet Tester': {
@@ -42,8 +44,75 @@
       },
     },
   },
+  'angle_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTAndroidCFI',
+      'ToTLinuxMSan',
+    ],
+  },
+  'app_shell_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'CrWinClang',
+      'CrWinClang(dbg)',
+      'CrWinClang(shared)',
+      'CrWinClang64',
+      'CrWinClang64(dbg)',
+      'CrWinClang64(dll)',
+      'CrWinClangLLD',
+      'CrWinClangLLD64',
+      'CrWinClngLLD64dbg',
+      'CrWinClngLLDdbg',
+      'ToTLinuxASan',
+      'ToTLinuxMSan',
+      'ToTLinuxUBSanVptr',
+      'ToTWin',
+      'ToTWin(dbg)',
+      'ToTWin(dll)',
+      'ToTWin64',
+      'ToTWin64(dbg)',
+      'ToTWin64(dll)',
+      'ToTWinCFI',
+      'ToTWinCFI64',
+      'ToTWinThinLTO64',
+    ],
+  },
+  'aura_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'CrWinClang',
+      'CrWinClang(dbg)',
+      'CrWinClang(shared)',
+      'CrWinClang64',
+      'CrWinClang64(dbg)',
+      'CrWinClang64(dll)',
+      'CrWinClangLLD',
+      'CrWinClangLLD64',
+      'CrWinClngLLD64dbg',
+      'CrWinClngLLDdbg',
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
+      'ToTWin',
+      'ToTWin(dbg)',
+      'ToTWin(dll)',
+      'ToTWin64',
+      'ToTWin64(dbg)',
+      'ToTWin64(dll)',
+      'ToTWinCFI',
+      'ToTWinCFI64',
+      'ToTWinThinLTO64',
+    ],
+  },
   'base_unittests': {
     'modifications': {
+      # chromium.android
       'KitKat Tablet Tester': {
         'swarming': {
           'hard_timeout': 300,
@@ -69,6 +138,12 @@
           'hard_timeout': 600,
         },
       },
+      # chromium.clang
+      'ToTMac': {
+        'swarming': {
+          'shards': 5,
+        },
+      },
     },
   },
   'breakpad_unittests': {
@@ -91,6 +166,11 @@
     },
   },
   'browser_tests': {
+    'remove_from': [
+      # chromium.clang
+      'CrWinAsanCov',
+      'ToTLinuxUBSanVptr',
+    ],
     'modifications': {
       'Win7 Tests (dbg)(1)': {
         'swarming': {
@@ -122,6 +202,56 @@
           'shards': 5,
         },
       },
+      # chromium.clang
+      'CFI Linux (icall)': {
+        'swarming': {
+          'shards': 5,
+        },
+      },
+      'CrWinClang(dbg)': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+      'CrWinClang64(dbg)': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+      'ToTLinux': {
+        'swarming': {
+          'shards': 5,
+        },
+      },
+      'ToTLinuxASan': {
+        'swarming': {
+          'shards': 5,
+        },
+      },
+      'ToTLinuxLLD': {
+        'swarming': {
+          'shards': 5,
+        },
+      },
+      'ToTWin(dbg)': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+      'ToTWin64(dbg)': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+      'ToTLinuxThinLTO': {
+        # TODO(kbr): remove this spurious filter.
+        'args': [
+          '--gtest_filter=-SaveType/SavePageMultiFrameBrowserTest.ObjectElements/0',
+        ],
+        'swarming': {
+          'shards': 10,
+        },
+      },
     },
   },
   'blink_heap_unittests': {
@@ -136,6 +266,11 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'ToTLinuxMSan',
       # On chromium.linux, unclear why these only run on "Linux Tests".
       'Cast Audio Linux',
       'Cast Linux',
@@ -171,6 +306,11 @@
   },
   'blink_platform_unittests': {
     'remove_from': [
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'ToTLinuxMSan',
       # On chromium.linux, unclear why these only run on "Linux Tests".
       'Linux Tests (dbg)(1)',
       'Linux Tests (dbg)(1)(32)',
@@ -234,9 +374,15 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
     ],
   },
   'capture_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTLinuxUBSanVptr',
+    ],
     'modifications': {
       'KitKat Tablet Tester': {
         'swarming': {
@@ -286,6 +432,9 @@
       # TODO(kbr): why are the cast unit tests not run on the Cast bots?!
       'Cast Audio Linux',
       'Cast Linux',
+      # chromium.clang
+      'ToTAndroidCFI',
+      'ToTLinuxThinLTO',
     ],
   },
   'cc_unittests': {
@@ -331,11 +480,43 @@
       'Mac',
     ],
   },
+  'chrome_elf_import_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'CrWinClang',
+      'CrWinClang(dbg)',
+      'CrWinClang(shared)',
+      'CrWinClang64',
+      'CrWinClang64(dbg)',
+      'CrWinClang64(dll)',
+      'CrWinClangLLD',
+      'CrWinClangLLD64',
+      'CrWinClngLLD64dbg',
+      'CrWinClngLLDdbg',
+      'ToTWin(dll)',
+      'ToTWin64',
+      'ToTWin64(dbg)',
+      'ToTWin64(dll)',
+      'ToTWinCFI',
+      'ToTWinCFI64',
+      'ToTWinThinLTO64',
+    ],
+  },
   'chrome_public_test_apk': {
     'remove_from': [
       # TODO(kbr): on chromium.android this removal looks like an accident.
       'Marshmallow Phone Tester (rel)',
+      # chromium.clang
+      'ToTAndroidCFI',
     ],
+    'key_removals': {
+      'ToTAndroid x64': [
+        'args',
+      ],
+    },
     'modifications': {
       'KitKat Phone Tester (dbg)': {
         'swarming': {
@@ -373,15 +554,25 @@
           'hard_timeout': 1800,
         },
       },
+      # chromium.clang
+      'ToTAndroid x64': {
+        'swarming': {
+          'shards': 1,
+          'hard_timeout': 0,
+        },
+      },
     },
   },
   'chrome_public_test_vr_apk': {
     'remove_from': [
+      # chromium.android
       'KitKat Phone Tester (dbg)',
       'KitKat Phone Tester (rel)',
       'KitKat Tablet Tester',
       'Lollipop Tablet Tester',
       'Marshmallow Tablet Tester',
+      # chromium.clang
+      'ToTAndroid x64',
     ],
     'modifications': {
       'Lollipop Phone Tester': {
@@ -412,7 +603,16 @@
     },
   },
   'chrome_sync_shell_test_apk': {
+    'key_removals': {
+      'ToTAndroid x64': [
+        'args',
+      ],
+      'ToTAndroidCFI': [
+        'args',
+      ],
+    },
     'modifications': {
+      # chromium.android
       'KitKat Phone Tester (dbg)': {
         'swarming': {
           'shards': 2,
@@ -433,6 +633,17 @@
           'hard_timeout': 1200,
         },
       },
+      # chromium.clang
+      'ToTAndroid x64': {
+        'swarming': {
+          'hard_timeout': 0,
+        },
+      },
+      'ToTAndroidCFI': {
+        'swarming': {
+          'hard_timeout': 0,
+        },
+      },
     },
   },
   'chromedriver_unittests': {
@@ -441,6 +652,9 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
     ],
   },
   'components_background_task_scheduler_junit_tests': {
@@ -462,6 +676,14 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTLinux',
+      'ToTLinuxASan',
+      'ToTLinuxLLD',
+      'ToTLinuxMSan',
+      'ToTLinuxUBSanVptr',
+      'ToTMac',
+      'ToTMacASan',
       # On chromium.linux, unclear why these aren't run on the Cast bots.
       'Cast Audio Linux',
       'Cast Linux',
@@ -494,6 +716,8 @@
   },
   'components_unittests': {
     'remove_from': [
+      # chromium.clang
+      'ToTLinuxUBSanVptr',
       # On chromium.linux, unclear why these aren't run on the Cast bots.
       'Cast Audio Linux',
       'Cast Linux',
@@ -531,24 +755,38 @@
       },
     },
   },
+  'compositor_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'CrWinClang',
+      'CrWinClang(dbg)',
+      'CrWinClang(shared)',
+      'CrWinClang64',
+      'CrWinClang64(dbg)',
+      'CrWinClang64(dll)',
+      'CrWinClangLLD',
+      'CrWinClangLLD64',
+      'CrWinClngLLD64dbg',
+      'CrWinClngLLDdbg',
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
+      'ToTWin',
+      'ToTWin(dbg)',
+      'ToTWin(dll)',
+      'ToTWin64',
+      'ToTWin64(dbg)',
+      'ToTWin64(dll)',
+      'ToTWinCFI',
+      'ToTWinCFI64',
+      'ToTWinThinLTO64',
+    ],
+  },
   'content_browsertests': {
     'modifications': {
-      'Cast Audio Linux': {
-        'args': [
-          '--test-launcher-filter-file=src/testing/buildbot/filters/cast-linux.content_browsertests.filter',
-        ],
-        'swarming': {
-          'can_use_on_swarming_builders': False,
-        },
-      },
-      'Cast Linux': {
-        'args': [
-          '--test-launcher-filter-file=src/testing/buildbot/filters/cast-linux.content_browsertests.filter',
-        ],
-        'swarming': {
-          'can_use_on_swarming_builders': False,
-        },
-      },
+      # chromium.android
       'KitKat Tablet Tester': {
         'swarming': {
           'hard_timeout': 1800,
@@ -605,10 +843,35 @@
           'shards': 2,
         },
       },
+      # chromium.clang
+      'ToTLinuxUBSanVptr': {
+        'swarming': {
+          'shards': 5,
+        },
+      },
+      # chromium.linux
+      'Cast Audio Linux': {
+        'args': [
+          '--test-launcher-filter-file=src/testing/buildbot/filters/cast-linux.content_browsertests.filter',
+        ],
+        'swarming': {
+          'can_use_on_swarming_builders': False,
+        },
+      },
+      'Cast Linux': {
+        'args': [
+          '--test-launcher-filter-file=src/testing/buildbot/filters/cast-linux.content_browsertests.filter',
+        ],
+        'swarming': {
+          'can_use_on_swarming_builders': False,
+        },
+      },
     },
   },
   'content_shell_crash_test': {
     'remove_from': [
+      # chromium.clang
+      'ToTMac',
       # On chromium.linux, unclear why these only run on "Linux Tests".
       'Linux Tests (dbg)(1)',
       'Linux Tests (dbg)(1)(32)',
@@ -624,7 +887,17 @@
     ],
   },
   'content_shell_test_apk': {
+    'key_removals': {
+      # chromium.clang
+      'ToTAndroid x64': [
+        'args',
+      ],
+      'ToTAndroidCFI': [
+        'args',
+      ],
+    },
     'modifications': {
+      # chromium.android
       'KitKat Tablet Tester': {
         'swarming': {
           'hard_timeout': 1200,
@@ -648,6 +921,19 @@
           'shards': 2,
         },
       },
+      # chromium.clang
+      'ToTAndroid x64': {
+        'swarming': {
+          'hard_timeout': 0,
+          'shards': 1,
+        },
+      },
+      'ToTAndroidCFI': {
+        'swarming': {
+          'hard_timeout': 0,
+          'shards': 1,
+        },
+      },
     },
   },
   'content_unittests': {
@@ -673,6 +959,15 @@
       },
     },
   },
+  'crashpad_tests': {
+    'remove_from': [
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'ToTMacASan',
+    ],
+  },
   'crypto_unittests': {
     'remove_from': [
        # TODO(dpranke) - remove this exception.
@@ -689,6 +984,8 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
       # TODO(kbr): on chromium.linux, it's unclear why these show up on "Cast
       # Audio Linux" at all, since they're supposed to be compiled out for
       # Chromecast.
@@ -696,11 +993,19 @@
   },
   'dbus_unittests': {
     'remove_from': [
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
+      # chromium.linux
       'Linux Tests (dbg)(1)(32)',
     ],
   },
   'device_unittests': {
     'remove_from': [
+      # chromium.clang
+      'ToTMac',
+      'ToTMacASan',
+      # chromium.win
       'Win7 Tests (dbg)(1)',
     ],
     'modifications': {
@@ -747,16 +1052,18 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
       # chromium.win
       'Win7 Tests (dbg)(1)',
     ],
   },
-  'dbus_unittests': {
-    'remove_from': [
-      'Linux Tests (dbg)(1)(32)',
-    ],
-  },
   'events_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
+    ],
     'modifications': {
       'KitKat Tablet Tester': {
         'swarming': {
@@ -777,6 +1084,11 @@
   },
   'extensions_browsertests': {
     'remove_from': [
+      # chromium.clang
+      'ToTLinux',
+      'ToTLinuxLLD',
+      'ToTMac',
+      'ToTMacASan',
       # On chromium.mac, unclear why these aren't run.
       'Mac10.10 Tests',
       'Mac10.11 Tests',
@@ -790,6 +1102,12 @@
       'Linux Tests (dbg)(1)(32)',
     ],
   },
+  'gcm_unit_tests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTAndroidCFI',
+    ],
+  },
   'gfx_unittests': {
     'remove_from': [
       # On chromium.android, unclear why these aren't run on all bots.
@@ -838,6 +1156,12 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTLinux',
+      'ToTLinuxASan',
+      'ToTLinuxLLD',
+      'ToTLinuxMSan',
+      'ToTLinuxUBSanVptr',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
@@ -870,6 +1194,48 @@
   },
   'gl_unittests': {
     'modifications': {
+      'CFI Linux (icall)': {
+        'args': [
+          '--use-gpu-in-tests',
+          '--no-xvfb',
+        ],
+        'swarming': {
+          'dimension_sets': [
+            {
+              'gpu': '10de:1cb3',
+              'os': 'Ubuntu',
+              'pool': 'Chrome-GPU',
+            },
+          ],
+        },
+        'use_xvfb': False,
+      },
+      'CFI Linux ToT': {
+        'args': [
+          '--use-gpu-in-tests',
+          '--no-xvfb',
+        ],
+        'swarming': {
+          'dimension_sets': [
+            {
+              'gpu': '10de:1cb3',
+              'os': 'Ubuntu',
+              'pool': 'Chrome-GPU',
+            },
+          ],
+        },
+        'use_xvfb': False,
+      },
+      'ToTAndroidCFI': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              'device_os': 'MMB29Q',
+              'device_type': 'bullhead',
+            },
+          ],
+        },
+      },
       'Lollipop Phone Tester': {
         'swarming': {
           'hard_timeout': 960,
@@ -887,6 +1253,16 @@
       },
     },
   },
+  'gn_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTLinux',
+      'ToTLinuxASan',
+      'ToTLinuxLLD',
+      'ToTLinuxMSan',
+      'ToTLinuxUBSanVptr',
+    ],
+  },
   'google_apis_unittests': {
     'remove_from': [
       # On chromium.android, unclear why these aren't run.
@@ -899,6 +1275,8 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
@@ -908,6 +1286,10 @@
   },
   'gpu_ipc_service_unittests': {
     'remove_from': [
+      # chromium.clang
+      'CrWinClngLLD64dbg',
+      'ToTLinuxLLD',
+      # chromium.linux
       'Linux Tests (dbg)(1)(32)',
     ],
     'modifications': {
@@ -961,8 +1343,43 @@
       'Mac10.11 Tests',
     ],
   },
+  'install_static_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'CrWinClang',
+      'CrWinClang(dbg)',
+      'CrWinClang(shared)',
+      'CrWinClang64',
+      'CrWinClang64(dbg)',
+      'CrWinClang64(dll)',
+      'CrWinClangLLD',
+      'CrWinClangLLD64',
+      'CrWinClngLLD64dbg',
+      'CrWinClngLLDdbg',
+      'ToTWin(dbg)',
+      'ToTWin(dll)',
+      'ToTWin64',
+      'ToTWin64(dbg)',
+      'ToTWin64(dll)',
+      'ToTWinCFI',
+      'ToTWinCFI64',
+      'ToTWinThinLTO64',
+    ],
+  },
   'interactive_ui_tests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTLinuxUBSanVptr',
+    ],
     'modifications': {
+      'CFI Linux (icall)': {
+        'swarming': {
+          'shards': 1,
+        },
+      },
       # Unclear why this isn't sharded.
       'Linux Tests': {
         'swarming': {
@@ -1055,6 +1472,8 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
     ],
   },
   'keyboard_unittests': {
@@ -1079,6 +1498,13 @@
       'Marshmallow 64 bit Tester',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroid x64',
+      'ToTLinux',
+      'ToTLinuxASan',
+      'ToTLinuxLLD',
+      'ToTLinuxThinLTO',
+      'ToTLinuxUBSanVptr',
       # On chromium.linux, unclear why these aren't run.
       'Linux Tests',
       'Linux Tests (dbg)(1)',
@@ -1108,6 +1534,13 @@
       'linux-chromeos-rel',
     ],
   },
+  'mac_installer_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTMac',
+      'ToTMacASan',
+    ],
+  },
   'media_blink_unittests': {
     'remove_from': [
       # On chromium.android, unclear why these aren't run on all bots.
@@ -1115,6 +1548,8 @@
       'KitKat Phone Tester (rel)',
       'KitKat Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroid x64',
     ],
     'modifications': {
       'Lollipop Tablet Tester': {
@@ -1137,6 +1572,10 @@
     ],
   },
   'media_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTLinuxUBSanVptr',
+    ],
     'modifications': {
       'KitKat Tablet Tester': {
         'swarming': {
@@ -1186,12 +1625,15 @@
       # TODO(dpranke) - remove this exception.
       'Linux Tests SANDBOX',
 
-      'Win7 Tests (dbg)(1)',
+      # chromium.clang
+      'ToTMac',
       # On chromium.mac, unclear why these only run on "Mac10.9 Tests".
       'Mac10.10 Tests',
       'Mac10.11 Tests',
       'Mac10.12 Tests',
       'Mac10.9 Tests (dbg)',
+      # chromium.win
+      'Win7 Tests (dbg)(1)',
     ],
   },
   'midi_unittests': {
@@ -1206,6 +1648,8 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
     ],
   },
   'mojo_common_unittests': {
@@ -1221,6 +1665,9 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
@@ -1246,6 +1693,9 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
@@ -1271,6 +1721,9 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
@@ -1296,6 +1749,9 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
@@ -1317,8 +1773,11 @@
       'Marshmallow 64 bit Tester',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroid x64',
     ],
     'modifications': {
+      # chromium.android
       'KitKat Phone Tester (dbg)': {
         'args': [
           '--gs-results-bucket=chromium-result-details',
@@ -1334,15 +1793,48 @@
           '--gs-results-bucket=chromium-result-details',
         ],
       },
+      # chromium.clang
+      'ToTAndroidCFI': {
+        'swarming': {
+          'hard_timeout': 0,
+        },
+      },
     },
   },
+  'nacl_helper_nonsfi_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'CFI Linux (icall)',
+      'CFI Linux ToT',
+      'ToTLinux (dbg)',
+      'ToTLinuxASan',
+      'ToTLinuxMSan',
+      'ToTLinuxThinLTO',
+      'ToTLinuxUBSanVptr',
+    ],
+  },
+  'nacl_loader_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
+    ],
+  },
   'native_theme_unittests': {
     'remove_from': [
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTMacASan',
+      # chromium.linux
       'Linux Tests (dbg)(1)(32)',
     ],
   },
   'net_unittests': {
+    'remove_from': [
+      'CrWinAsanCov',
+    ],
     'modifications': {
+      # chromium.android
       'KitKat Tablet Tester': {
         'swarming': {
           'hard_timeout': 1800,
@@ -1376,6 +1868,12 @@
           'hard_timeout': 1800,
         },
       },
+      # chromium.clang
+      'ToTLinuxASan': {
+        'swarming': {
+          'shards': 4,
+        },
+      },
     },
   },
   'printing_unittests': {
@@ -1397,6 +1895,8 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
       # On chromium.linux, unclear why these aren't run on 32-bit.
       'Linux Tests (dbg)(1)(32)',
     ],
@@ -1419,6 +1919,9 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTAndroid x64',
+      'ToTAndroidCFI',
       # On chromium.linux, unclear why these aren't run on 32-bit.
       'Linux Tests (dbg)(1)(32)',
     ],
@@ -1442,6 +1945,9 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroid x64',
+      'ToTAndroidCFI',
     ],
     'modifications': {
       'Marshmallow 64 bit Tester': {
@@ -1470,6 +1976,9 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'ToTAndroid x64',
+      'ToTAndroidCFI',
       # On chromium.linux, unclear why these aren't run on 32-bit.
       'Linux Tests (dbg)(1)(32)',
     ],
@@ -1485,6 +1994,9 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroid x64',
+      'ToTAndroidCFI',
     ],
     'modifications': {
       'Marshmallow 64 bit Tester': {
@@ -1578,6 +2090,15 @@
       'Marshmallow 64 bit Tester',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'CFI Linux (icall)',
+      'CFI Linux ToT',
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'ToTAndroidASan',
+      'ToTLinuxMSan',
+      'ToTLinuxThinLTO',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
@@ -1661,6 +2182,8 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
@@ -1704,6 +2227,13 @@
     },
   },
   'storage_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxLLD',
+      'ToTLinuxThinLTO',
+      'ToTLinuxUBSanVptr',
+    ],
     'modifications': {
       # chromium.android
       'KitKat Phone Tester (dbg)': {
@@ -1912,6 +2442,11 @@
     },
   },
   'ui_touch_selection_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
+    ],
     'modifications': {
       'KitKat Tablet Tester': {
         'swarming': {
@@ -1927,11 +2462,14 @@
   },
   'unit_tests': {
     'remove_from': [
+      # chromium.clang
+      'CrWinAsanCov',
       # On chromium.linux, unclear why these aren't run on Cast.
       'Cast Audio Linux',
       'Cast Linux',
     ],
     'modifications': {
+      # chromium.android
       'KitKat Tablet Tester': {
         'swarming': {
           'hard_timeout': 1200,
@@ -1968,6 +2506,12 @@
           'shards': 2,
         },
       },
+      # chromium.clang
+      'ToTLinuxASan': {
+        'swarming': {
+          'shards': 2,
+        },
+      }
     },
   },
   'url_unittests': {
@@ -1982,10 +2526,16 @@
       'Marshmallow Phone Tester (rel)',
       'Marshmallow Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroidCFI',
     ],
   },
   'views_unittests': {
     'remove_from': [
+      # chromium.clang
+      'ToTLinuxASan',
+      'ToTMacASan',
+      # chromium.linux
       'Linux Tests (dbg)(1)(32)',
     ],
   },
@@ -2003,6 +2553,16 @@
     'remove_from': [
       # On chromium.android, unclear why these aren't run on all bots.
       'Nougat Phone Tester',
+      # chromium.clang
+      'CFI Linux (icall)',
+      'CFI Linux ToT',
+      'ToTAndroid x64',
+      'ToTAndroidASan',
+      'ToTLinux',
+      'ToTLinuxASan',
+      'ToTLinuxLLD',
+      'ToTLinuxThinLTO',
+      'ToTLinuxUBSanVptr',
       # chromium.win
       'Win10 Tests x64',
     ],
@@ -2048,6 +2608,8 @@
       'KitKat Phone Tester (dbg)',
       'KitKat Phone Tester (rel)',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroid x64',
       # chromium.win
       'Win 7 Tests x64 (1)',
       'Win10 Tests x64',
@@ -2075,12 +2637,37 @@
       },
     },
   },
+  'vr_pixeltests': {
+    'remove_from': [
+      # chromium.android
+      'KitKat Phone Tester (dbg)',
+      'KitKat Phone Tester (rel)',
+      'KitKat Tablet Tester',
+      'Lollipop Phone Tester',
+      'Lollipop Tablet Tester',
+      'Marshmallow 64 bit Tester',
+      'Marshmallow Phone Tester (rel)',
+      'Marshmallow Tablet Tester',
+      # chromium.clang
+      'ToTAndroid x64',
+      # chromium.win
+      'Win 7 Tests x64 (1)',
+      'Win10 Tests x64',
+      'Win7 Tests (1)',
+      'Win7 Tests (dbg)(1)',
+    ],
+  },
   'webkit_layout_tests': {
     'remove_from': [
+      # chromium.clang
+      'ToTMac',
+      # chromium.linux
       'Linux Tests (dbg)(1)',
       'Linux Tests (dbg)(1)(32)',
+      # chromium.mac
       'Mac10.9 Tests',
       'Mac10.9 Tests (dbg)',
+      # chromium.win
       'Win 7 Tests x64 (1)',
       'Win10 Tests x64',
       'Win7 Tests (1)',
@@ -2174,6 +2761,8 @@
   },
   'webkit_python_tests': {
     'remove_from': [
+      # chromium.clang
+      'ToTMac',
       # On chromium.linux, unclear why these only run on "Linux Tests".
       'Linux Tests (dbg)(1)',
       'Linux Tests (dbg)(1)(32)',
@@ -2202,6 +2791,13 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'ToTAndroidCFI',
+      'ToTLinuxMSan',
+      'ToTLinuxThinLTO',
       # On chromium.linux, unclear why these only run on "Linux Tests".
       'Cast Audio Linux',
       'Cast Linux',
@@ -2224,8 +2820,17 @@
       'KitKat Phone Tester (rel)',
       'KitKat Tablet Tester',
       'Nougat Phone Tester',
+      # chromium.clang
+      'ToTAndroid x64',
     ],
+    'key_removals': {
+      # chromium.clang
+      'ToTAndroidCFI': [
+        'args',
+      ],
+    },
     'modifications': {
+      # chromium.android
       'Lollipop Phone Tester': {
         'swarming': {
           'shards': 6,
@@ -2257,6 +2862,35 @@
       },
     },
   },
+  'wm_unittests': {
+    'remove_from': [
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'CrWinClang',
+      'CrWinClang(dbg)',
+      'CrWinClang(shared)',
+      'CrWinClang64',
+      'CrWinClang64(dbg)',
+      'CrWinClang64(dll)',
+      'CrWinClangLLD',
+      'CrWinClangLLD64',
+      'CrWinClngLLD64dbg',
+      'CrWinClngLLDdbg',
+      'ToTLinuxASan',
+      'ToTLinuxUBSanVptr',
+      'ToTWin',
+      'ToTWin(dbg)',
+      'ToTWin(dll)',
+      'ToTWin64',
+      'ToTWin64(dbg)',
+      'ToTWin64(dll)',
+      'ToTWinCFI',
+      'ToTWinCFI64',
+      'ToTWinThinLTO64',
+    ],
+  },
   'wtf_unittests': {
     'remove_from': [
       # On chromium.android, unclear why these aren't run.
@@ -2273,6 +2907,12 @@
       'Linux ChromiumOS Tests (dbg)(1)',
       'linux-chromeos-dbg',
       'linux-chromeos-rel',
+      # chromium.clang
+      'CrWinAsan',
+      'CrWinAsan(dll)',
+      'CrWinAsanCov',
+      'ToTAndroidCFI',
+      'ToTLinuxMSan',
       # On chromium.linux, unclear why these only run on "Linux Tests".
       'Cast Audio Linux',
       'Cast Linux',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 21e9ccd..12b11e9 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -16,6 +16,13 @@
 
 {
   # Test suites.
+
+  'android_clang_and_fyi_specific_gtests': {
+    # TODO(kbr): this entire test suite should probably be removed.
+    'cast_unittests': {},
+    'gcm_unit_tests': {},
+  },
+
   'android_l_cts_tests': {
     'arch': 'arm64',
     'platform': 'L',
@@ -130,14 +137,22 @@
     },
   },
 
+  'angle_gtests': {
+    'angle_unittests': {},
+  },
+
   'aura_gtests': {
     'app_shell_unittests': {},
     'aura_unittests': {},
     'compositor_unittests': {},
-    'keyboard_unittests': {},
     'wm_unittests': {},
   },
 
+  'aura_non_clang_gtests': {
+    # TODO(kbr): merge back into 'aura_gtests'.
+    'keyboard_unittests': {},
+  },
+
   'cast_audio_specific_chromium_gtests': {
     'cast_audio_backend_unittests': {},
     'cast_base_unittests': {},
@@ -151,20 +166,42 @@
     'cast_graphics_unittests': {},
   },
 
+  'check_gn_headers_script': {
+    'check_gn_headers': {
+      'script': 'check_gn_headers.py',
+    }
+  },
+
   'check_network_annotations_script': {
     'check_network_annotations': {
       'script': 'check_network_annotations.py',
     },
   },
 
+  'chromium_android_asan_gtests': {
+    # TODO(kbr): reduce duplication among these tests, and with other
+    # test suites.
+    'base_unittests': {
+      'args': [
+        '--tool=asan',
+      ],
+    },
+    'components_browsertests': {
+      'args': [
+        '--tool=asan',
+      ],
+    },
+  },
+
+  'chromium_android_asan_junit_tests': {
+    'base_junit_tests': {},
+  },
+
   'chromium_gtests': {
     'base_unittests': {},
     'blink_heap_unittests': {},
-    'boringssl_crypto_tests': {},
-    'boringssl_ssl_tests': {},
     'cacheinvalidation_unittests': {},
     'capture_unittests': {},
-    'components_browsertests': {},
     'components_unittests': {
       'android_swarming': {
         'hard_timeout': 900,
@@ -183,43 +220,20 @@
       },
     },
     'crypto_unittests': {},
-    'gin_unittests': {},
     'google_apis_unittests': {},
     'gpu_ipc_service_unittests': {},
     'gpu_unittests': {},
     'ipc_tests': {},
     'jingle_unittests': {},
-    'libjingle_xmpp_unittests': {},
     'media_blink_unittests': {},
     'media_unittests': {},
     'midi_unittests': {},
-    'mojo_common_unittests': {
-      'android_swarming': {
-        'hard_timeout': 60,
-      },
-    },
-    'mojo_public_bindings_unittests': {
-      'android_swarming': {
-        'hard_timeout': 60,
-      },
-    },
-    'mojo_public_system_unittests': {
-      'android_swarming': {
-        'hard_timeout': 60,
-      },
-    },
-    'mojo_system_unittests': {
-      'android_swarming': {
-        'hard_timeout': 180,
-      },
-    },
     'net_unittests': {
       'android_swarming': {
         'hard_timeout': 900,
         'shards': 3,
       },
     },
-    'service_manager_unittests': {},
     'services_unittests': {
       'android_swarming': {
         'hard_timeout': 120,
@@ -227,7 +241,6 @@
     },
     'skia_unittests': {},
     'sql_unittests': {},
-    'storage_unittests': {},
     'ui_base_unittests': {},
     'unit_tests': {
       'android_swarming': {
@@ -240,12 +253,18 @@
   },
 
   'chromium_gtests_for_devices_with_graphical_output': {
-    'cc_unittests': {},
     'device_unittests': {},
+    'remoting_unittests': {},
+  },
+
+  'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output': {
+    'viz_unittests': {},
+  },
+
+  'chromium_gtests_for_non_clang_win_devices_with_graphical_output': {
+    'cc_unittests': {},
     'display_unittests': {},
     'gfx_unittests': {},
-    'remoting_unittests': {},
-    'viz_unittests': {},
   },
 
   'chromium_junit_tests': {
@@ -285,6 +304,15 @@
     }
   },
 
+  'clang_gl_gtests': {
+    'gl_unittests': {},
+  },
+
+  'clang_linux_and_mac_gtests': {
+    # TODO: merge back into non_android_chromium_gtests.
+    'interactive_ui_tests': {},
+  },
+
   'cronet_gtests': {
     'cronet_sample_test_apk': {
       'swarming': {
@@ -375,11 +403,13 @@
     },
   },
 
-  'linux_and_chromeos_specific_chromium_gtests': {
+  'linux_and_chromeos_non_clang_specific_chromium_gtests': {
+    # TOOD(kbr): rename back to linux_and_chromeos_specific_chromium_gtests.
     'media_service_unittests': {},
   },
 
-  'linux_and_mac_specific_chromium_gtests': {
+  'linux_and_mac_non_clang_specific_chromium_gtests': {
+    # TODO(kbr): merge back into linux_and_mac_specific_chromium_gtests.
     'snapshot_unittests': {},
   },
 
@@ -509,14 +539,22 @@
     'ui_chromeos_unittests': {},
   },
 
+  'linux_clang_and_fyi_specific_chromium_gtests': {
+    # TODO(kbr): needs to be merged with linux_chromeos_specific_gtests.
+    'nacl_helper_nonsfi_unittests': {},
+  },
+
   'linux_flavor_specific_chromium_gtests': {
     # Android, Chrome OS and Linux
     'sandbox_linux_unittests': {},
   },
 
+  'linux_incl_clang_specific_chromium_gtests': {
+    'dbus_unittests': {},
+  },
+
   'linux_specific_chromium_gtests': {
     # TODO(kbr): unclear why some of these aren't run more broadly.
-    'dbus_unittests': {},
     'filesystem_service_unittests': {},
     'leveldb_service_unittests': {},
     'site_per_process_browser_tests': {
@@ -572,31 +610,16 @@
   },
 
   'non_android_chromium_gtests': {
-    'accessibility_unittests': {},
     'browser_tests': {
       'swarming': {
         'shards': 10,
       },
     },
-    # TODO(kbr): cast_unittests is run on Android x86 testers on
-    # chromium.android.fyi.
-    'cast_unittests': {},
-    'chrome_app_unittests': {},
-    'chromedriver_unittests': {},
-    'extensions_browsertests': {},
     'extensions_unittests': {},
-    'interactive_ui_tests': {
-      'swarming': {
-        'shards': 2,
-      },
-    },
-    'message_center_unittests': {},
-    'nacl_loader_unittests': {},
     'native_theme_unittests': {},
     'pdf_unittests': {},
     'ppapi_unittests': {},
     'printing_unittests': {},
-    'sync_integration_tests': {},
     'views_unittests': {},
   },
 
@@ -605,19 +628,105 @@
   },
 
   'non_android_and_cast_and_chromeos_chromium_gtests': {
-    'battor_agent_unittests': {},
     'blink_platform_unittests': {},
-    'gn_unittests': {},
+  },
+
+  'non_android_and_cast_and_chromeos_and_clang_chromium_gtests': {
+    'battor_agent_unittests': {},
     'headless_browsertests': {},
     'headless_unittests': {},
   },
 
+  'non_android_and_cast_and_chromeos_and_clang_android_mac_win_chromium_gtests': {
+    # TODO(kbr): merge back into non_android_and_cast_and_chromeos_chromium_gtests.
+    'gn_unittests': {},
+  },
+
+  'non_android_and_clang_linux_mac_chromium_gtests': {
+    # TODO(kbr): merge back into non_android_chromium_gtests.
+    'chrome_app_unittests': {},
+  },
+
+  'non_android_and_clang_linux_mac_win_chromium_gtests': {
+    # TODO(kbr): merge back into non_android_chromium_gtests.
+    'interactive_ui_tests': {
+      'swarming': {
+        'shards': 2,
+      },
+    },
+    'message_center_unittests': {},
+  },
+
+  'non_android_and_clang_linux_win_chromium_gtests': {
+    'sync_integration_tests': {},
+  },
+
+  'non_android_and_clang_mac_win_chromium_gtests': {
+    # TODO(kbr): merge back into non_android_chromium_gtests.
+    'nacl_loader_unittests': {},
+  },
+
+  'non_android_and_clang_win_chromium_gtests': {
+    # TODO(kbr): merge back into non_android_chromium_gtests.
+    'accessibility_unittests': {},
+    # TODO(kbr): cast_unittests is run on Android x86 testers on
+    # chromium.android.fyi and chromuim.clang. Figure out whether this
+    # is correct.
+    'cast_unittests': {},
+    'chromedriver_unittests': {},
+    'extensions_browsertests': {},
+  },
+
+  'non_clang_chromium_gtests': {
+    # TODO(kbr): merge back into chromium_gtests.
+    'boringssl_crypto_tests': {},
+    'boringssl_ssl_tests': {},
+    'libjingle_xmpp_unittests': {},
+    'service_manager_unittests': {},
+  },
+
+  'non_clang_android_mac_win_chromium_gtests': {
+    # TODO(kbr): merge back into chromium_gtests.
+    'gin_unittests': {},
+  },
+
+  'non_clang_mac_win_chromium_gtests': {
+    # TODO(kbr): merge back into chromium_gtests.
+    'storage_unittests': {},
+  },
+
+  'non_clang_win_chromium_gtests': {
+    # TODO(kbr): merge back into chromium_gtests.
+    'components_browsertests': {},
+    'mojo_common_unittests': {
+      'android_swarming': {
+        'hard_timeout': 60,
+      },
+    },
+    'mojo_public_bindings_unittests': {
+      'android_swarming': {
+        'hard_timeout': 60,
+      },
+    },
+    'mojo_public_system_unittests': {
+      'android_swarming': {
+        'hard_timeout': 60,
+      },
+    },
+    'mojo_system_unittests': {
+      'android_swarming': {
+        'hard_timeout': 180,
+      },
+    },
+  },
+
   'non_linux_chromium_gtests': {
     'crashpad_tests': {},
   },
 
-  'non_mac_chromium_gtests': {
+  'non_mac_non_clang_win_chromium_gtests': {
     # It's unclear why at least some of these aren't run on macOS.
+    # TODO(kbr): rename this back to non_mac_chromium_gtests.
     'events_unittests': {},
     'latency_unittests': {},
     'ui_touch_selection_unittests': {},
@@ -648,10 +757,26 @@
     },
   },
 
-  'vr_platform_specific_chromium_gtests': {
+  'viz_gtests': {
+    'viz_content_browsertests': {
+      'args': [
+        '--enable-viz',
+        '--test-launcher-filter-file=../../testing/buildbot/filters/viz.content_browsertests.filter'
+      ],
+      'swarming': {
+        'shards': 2,
+      },
+      'test': 'content_browsertests',
+    },
+  },
+
+  # TODO(kbr): rename this back to vr_platform_specific_chromium_gtests and
+  # run it on Win/Clang.
+  'vr_platform_specific_non_clang_win_chromium_gtests': {
     # Only run on platforms that intend to support WebVR in the near
     # future.
     'vr_common_unittests': {},
+    'vr_pixeltests': {},
   },
 
   'webview_ui_instrumentation_tests': {
@@ -717,14 +842,37 @@
   },
 
   # Composition test suites.
+  'chromium_android_clang_and_gl_gtests': [
+    'android_clang_and_fyi_specific_gtests',
+    'android_specific_chromium_gtests',
+    'angle_gtests',
+    'chromium_gtests',
+    'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
+    'clang_gl_gtests',
+    'linux_and_android_specific_chromium_gtests',
+    'linux_flavor_specific_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
+    'vr_platform_specific_non_clang_win_chromium_gtests',
+  ],
+
   'chromium_android_gtests': [
     'android_specific_chromium_gtests',
     'chromium_gtests',
     'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
     'linux_and_android_specific_chromium_gtests',
     'linux_flavor_specific_chromium_gtests',
-    'non_mac_chromium_gtests',
-    'vr_platform_specific_chromium_gtests',
+    'non_clang_android_mac_win_chromium_gtests',
+    'non_clang_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
+    'vr_platform_specific_non_clang_win_chromium_gtests',
   ],
 
   'chromium_isolated_scripts': [
@@ -736,6 +884,10 @@
     'cast_audio_specific_chromium_gtests',
     'chromium_gtests',
     'linux_flavor_specific_chromium_gtests',
+    'non_clang_android_mac_win_chromium_gtests',
+    'non_clang_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
   ],
 
   'chromium_linux_cast_video_gtests': [
@@ -743,21 +895,88 @@
     'cast_video_specific_chromium_gtests',
     'chromium_gtests',
     'linux_flavor_specific_chromium_gtests',
+    'non_clang_android_mac_win_chromium_gtests',
+    'non_clang_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
+  ],
+
+  'chromium_linux_clang_gtests': [
+    'angle_gtests',
+    'aura_gtests',
+    'chromium_gtests',
+    'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
+    'clang_linux_and_mac_gtests',
+    'linux_clang_and_fyi_specific_chromium_gtests',
+    'linux_flavor_specific_chromium_gtests',
+    'linux_incl_clang_specific_chromium_gtests',
+    'non_android_chromium_gtests',
+    'non_android_and_cast_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_android_mac_win_chromium_gtests',
+    'non_android_and_cast_and_chromeos_chromium_gtests',
+    'non_android_and_clang_mac_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_clang_android_mac_win_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
+  ],
+
+  'chromium_linux_clang_and_gl_gtests': [
+    'angle_gtests',
+    'aura_gtests',
+    'chromium_gtests',
+    'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
+    'clang_gl_gtests',
+    'clang_linux_and_mac_gtests',
+    'linux_clang_and_fyi_specific_chromium_gtests',
+    'linux_flavor_specific_chromium_gtests',
+    'linux_incl_clang_specific_chromium_gtests',
+    'non_android_chromium_gtests',
+    'non_android_and_cast_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_android_mac_win_chromium_gtests',
+    'non_android_and_cast_and_chromeos_chromium_gtests',
+    'non_android_and_clang_mac_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_clang_android_mac_win_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
   ],
 
   'chromium_linux_gtests': [
     'aura_gtests',
+    'aura_non_clang_gtests',
     'chromium_gtests',
     'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
     'linux_and_android_specific_chromium_gtests',
-    'linux_and_chromeos_specific_chromium_gtests',
-    'linux_and_mac_specific_chromium_gtests',
+    'linux_and_chromeos_non_clang_specific_chromium_gtests',
+    'linux_and_mac_non_clang_specific_chromium_gtests',
     'linux_flavor_specific_chromium_gtests',
+    'linux_incl_clang_specific_chromium_gtests',
     'linux_specific_chromium_gtests',
     'non_android_chromium_gtests',
     'non_android_and_cast_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_android_mac_win_chromium_gtests',
     'non_android_and_cast_and_chromeos_chromium_gtests',
-    'non_mac_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_chromium_gtests',
+    'non_android_and_clang_linux_mac_chromium_gtests',
+    'non_android_and_clang_linux_mac_win_chromium_gtests',
+    'non_android_and_clang_linux_win_chromium_gtests',
+    'non_android_and_clang_mac_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_clang_android_mac_win_chromium_gtests',
+    'non_clang_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
+    'viz_gtests',
   ],
 
   'linux_chromeos_dbg_gtests': [
@@ -767,16 +986,25 @@
     #   - non_android_and_cast_and_chromeos_chromium_gtests
     #   + linux_chromeos_specific_gtests
     'aura_gtests',
+    'aura_non_clang_gtests',
     'chromium_gtests',
     'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
     'linux_and_android_specific_chromium_gtests',
-    'linux_and_chromeos_specific_chromium_gtests',
-    'linux_and_mac_specific_chromium_gtests',
+    'linux_and_chromeos_non_clang_specific_chromium_gtests',
+    'linux_and_mac_non_clang_specific_chromium_gtests',
     'linux_chromeos_specific_gtests',
     'linux_flavor_specific_chromium_gtests',
     'non_android_chromium_gtests',
     'non_android_and_cast_chromium_gtests',
-    'non_mac_chromium_gtests',
+    'non_android_and_clang_linux_mac_win_chromium_gtests',
+    'non_android_and_clang_linux_mac_chromium_gtests',
+    'non_android_and_clang_linux_win_chromium_gtests',
+    'non_android_and_clang_mac_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
   ],
 
   'linux_chromeos_rel_gtests': [
@@ -784,17 +1012,26 @@
     #   linux_chromeos_dbg_gtests
     #   + linux_chromeos_rel_specific_gtests
     'aura_gtests',
+    'aura_non_clang_gtests',
     'chromium_gtests',
     'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
     'linux_and_android_specific_chromium_gtests',
-    'linux_and_chromeos_specific_chromium_gtests',
-    'linux_and_mac_specific_chromium_gtests',
+    'linux_and_chromeos_non_clang_specific_chromium_gtests',
+    'linux_and_mac_non_clang_specific_chromium_gtests',
     'linux_chromeos_specific_gtests',
     'linux_chromeos_rel_specific_gtests',
     'linux_flavor_specific_chromium_gtests',
     'non_android_chromium_gtests',
     'non_android_and_cast_chromium_gtests',
-    'non_mac_chromium_gtests',
+    'non_android_and_clang_linux_mac_win_chromium_gtests',
+    'non_android_and_clang_linux_mac_chromium_gtests',
+    'non_android_and_clang_linux_win_chromium_gtests',
+    'non_android_and_clang_mac_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
   ],
 
   'chromium_linux_isolated_scripts': [
@@ -803,27 +1040,83 @@
     'telemetry_perf_unittests_isolated_scripts',
   ],
 
-  'chromium_mac_gtests': [
+  'chromium_mac_clang_gtests': [
+    'angle_gtests',
     'chromium_gtests',
     'chromium_gtests_for_devices_with_graphical_output',
-    'linux_and_mac_specific_chromium_gtests',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
+    'clang_linux_and_mac_gtests',
     'mac_specific_chromium_gtests',
     'non_android_chromium_gtests',
     'non_android_and_cast_chromium_gtests',
     'non_android_and_cast_and_chromeos_chromium_gtests',
+    'non_android_and_clang_linux_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
     'non_linux_chromium_gtests',
   ],
 
-  'chromium_win_gtests': [
+  'chromium_mac_gtests': [
+    'chromium_gtests',
+    'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
+    'linux_and_mac_non_clang_specific_chromium_gtests',
+    'mac_specific_chromium_gtests',
+    'non_android_chromium_gtests',
+    'non_android_and_cast_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_android_mac_win_chromium_gtests',
+    'non_android_and_cast_and_chromeos_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_chromium_gtests',
+    'non_android_and_clang_linux_mac_chromium_gtests',
+    'non_android_and_clang_linux_mac_win_chromium_gtests',
+    'non_android_and_clang_linux_win_chromium_gtests',
+    'non_android_and_clang_mac_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_clang_android_mac_win_chromium_gtests',
+    'non_clang_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
+    'non_linux_chromium_gtests',
+  ],
+
+  'chromium_win_clang_gtests': [
+    'angle_gtests',
     'aura_gtests',
     'chromium_gtests',
     'chromium_gtests_for_devices_with_graphical_output',
     'non_android_chromium_gtests',
     'non_android_and_cast_chromium_gtests',
     'non_android_and_cast_and_chromeos_chromium_gtests',
+    'non_android_and_clang_linux_mac_chromium_gtests',
     'non_linux_chromium_gtests',
-    'non_mac_chromium_gtests',
-    'vr_platform_specific_chromium_gtests',
+    'win_specific_chromium_gtests',
+  ],
+
+  'chromium_win_gtests': [
+    'aura_gtests',
+    'aura_non_clang_gtests',
+    'chromium_gtests',
+    'chromium_gtests_for_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_mac_win_devices_with_graphical_output',
+    'chromium_gtests_for_non_clang_win_devices_with_graphical_output',
+    'non_android_chromium_gtests',
+    'non_android_and_cast_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_android_mac_win_chromium_gtests',
+    'non_android_and_cast_and_chromeos_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_chromium_gtests',
+    'non_android_and_clang_linux_mac_chromium_gtests',
+    'non_android_and_clang_linux_mac_win_chromium_gtests',
+    'non_android_and_clang_linux_win_chromium_gtests',
+    'non_android_and_clang_mac_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_clang_android_mac_win_chromium_gtests',
+    'non_clang_chromium_gtests',
+    'non_clang_mac_win_chromium_gtests',
+    'non_clang_win_chromium_gtests',
+    'non_linux_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
+    'vr_platform_specific_non_clang_win_chromium_gtests',
     'win_specific_chromium_gtests',
   ],
 
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index f64067c1f..e0b6a81 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -369,6 +369,230 @@
     },
   },
   {
+    'name': 'chromium.clang',
+    'machines': {
+      'CFI Linux (icall)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_clang_and_gl_gtests',
+        },
+      },
+      'CFI Linux ToT': {
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_clang_and_gl_gtests',
+        },
+      },
+      'CrWinAsan': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinAsan(dll)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinAsanCov': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClang': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClang(dbg)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClang(shared)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClang64': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClang64(dbg)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClang64(dll)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClangLLD': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClangLLD64': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClngLLD64dbg': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'CrWinClngLLDdbg': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTAndroid': {
+        'additional_compile_targets': [
+          'all',
+        ],
+      },
+      'ToTAndroid x64': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              'device_type': 'coho',
+            },
+          ],
+        },
+        'test_suites': {
+          'gtest_tests': 'chromium_android_clang_and_gl_gtests',
+        },
+      },
+      'ToTAndroid64': {
+        'additional_compile_targets': [
+          'all',
+        ],
+      },
+      'ToTAndroidASan': {
+        'test_suites': {
+          'gtest_tests': 'chromium_android_asan_gtests',
+          'junit_tests': 'chromium_android_asan_junit_tests',
+        },
+        'use_swarming': False,
+      },
+      'ToTAndroidCFI': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              'device_os': 'MMB29Q',
+              'device_type': 'bullhead',
+            },
+          ],
+        },
+        'test_suites': {
+          'gtest_tests': 'chromium_android_clang_and_gl_gtests',
+        },
+      },
+      'ToTLinux': {
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_clang_gtests',
+          'scripts': 'check_gn_headers_script',
+        },
+      },
+      'ToTLinux (dbg)': {
+        'additional_compile_targets': [
+          'all',
+        ],
+      },
+      'ToTLinuxASan': {
+        'additional_compile_targets': [
+          'all',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_clang_gtests',
+        },
+      },
+      'ToTLinuxLLD': {
+        'additional_compile_targets': [
+          'all',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_clang_gtests',
+        },
+      },
+      'ToTLinuxMSan': {
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_clang_gtests',
+        },
+      },
+      'ToTLinuxThinLTO': {
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_clang_and_gl_gtests',
+        },
+        'use_swarming': False,
+      },
+      'ToTLinuxUBSanVptr': {
+        'additional_compile_targets': [
+          'all',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_clang_gtests',
+        },
+      },
+      'ToTMac': {
+        'test_suites': {
+          'gtest_tests': 'chromium_mac_clang_gtests',
+          'isolated_scripts': 'chromium_isolated_scripts',
+        },
+      },
+      'ToTMacASan': {
+        'test_suites': {
+          'gtest_tests': 'chromium_mac_clang_gtests',
+        },
+      },
+      'ToTWin': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTWin(dbg)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTWin(dll)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTWin64': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTWin64(dbg)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTWin64(dll)': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTWinCFI': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTWinCFI64': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+      'ToTWinThinLTO64': {
+        'test_suites': {
+          'gtest_tests': 'chromium_win_clang_gtests',
+        },
+      },
+    },
+  },
+  {
     'name': 'chromium.linux',
     'machines': {
       'Fuchsia ARM64': {
diff --git a/third_party/WebKit/API_OWNERS b/third_party/WebKit/API_OWNERS
index 13300c6..72f5306 100644
--- a/third_party/WebKit/API_OWNERS
+++ b/third_party/WebKit/API_OWNERS
@@ -5,7 +5,6 @@
 bratell@opera.com
 chrishtr@chromium.org
 darin@chromium.org
-dglazkov@chromium.org
 foolip@chromium.org
 jochen@chromium.org
 mkwst@chromium.org
diff --git a/third_party/WebKit/LayoutTests/fast/table/collapsed-border-overflow-hidden-expected.html b/third_party/WebKit/LayoutTests/fast/table/collapsed-border-overflow-hidden-expected.html
new file mode 100644
index 0000000..10d5307
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/table/collapsed-border-overflow-hidden-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<style>
+table {
+  border-collapse: collapse;
+  border: 20px solid green;
+}
+td {
+  width: 100px;
+  height: 100px;
+}
+</style>
+Table with overflow: hidden should not clip off outer halves of collapsed borders. Passes if no red.
+<table>
+  <tr><td></td></tr>
+</table>
diff --git a/third_party/WebKit/LayoutTests/fast/table/collapsed-border-overflow-hidden.html b/third_party/WebKit/LayoutTests/fast/table/collapsed-border-overflow-hidden.html
new file mode 100644
index 0000000..b4d5daa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/table/collapsed-border-overflow-hidden.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+table {
+  border-collapse: collapse;
+  position: absolute;
+  border-width: 20px;
+  border-style: solid;
+}
+td {
+  width: 100px;
+  height: 100px;
+}
+</style>
+Table with overflow: hidden should not clip off outer halves of collapsed borders. Passes if no red.
+<table style="border-color: red">
+<tr><td></td></tr>
+</table>
+<table style="border-color: green; overflow: hidden">
+<tr><td></td></tr>
+</table>
diff --git a/third_party/WebKit/Source/core/OWNERS b/third_party/WebKit/Source/core/OWNERS
index d7ed9ecd..c890acc 100644
--- a/third_party/WebKit/Source/core/OWNERS
+++ b/third_party/WebKit/Source/core/OWNERS
@@ -11,7 +11,6 @@
 cbiesinger@chromium.org
 chrishtr@chromium.org
 dcheng@chromium.org
-dglazkov@chromium.org
 # dtapuska reviews input-related changes
 dtapuska@chromium.org
 dgozman@chromium.org
diff --git a/third_party/WebKit/Source/core/layout/LayoutTable.cpp b/third_party/WebKit/Source/core/layout/LayoutTable.cpp
index c67178c..3196188 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTable.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTable.cpp
@@ -1475,6 +1475,14 @@
 LayoutRect LayoutTable::OverflowClipRect(
     const LayoutPoint& location,
     OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
+  if (ShouldCollapseBorders()) {
+    // Though the outer halves of the collapsed borders are considered as the
+    // the border area of the table by means of the box model, they are actually
+    // contents of the table and should not be clipped off. The overflow clip
+    // rect is BorderBoxRect() + location.
+    return LayoutRect(location, Size());
+  }
+
   LayoutRect rect =
       LayoutBlock::OverflowClipRect(location, overlay_scrollbar_clip_behavior);
 
diff --git a/third_party/WebKit/Source/core/page/scrolling/SnapCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/SnapCoordinator.cpp
index 89c84ed1..1b8176e0 100644
--- a/third_party/WebKit/Source/core/page/scrolling/SnapCoordinator.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/SnapCoordinator.cpp
@@ -260,9 +260,15 @@
   const ComputedStyle* container_style = snap_container.Style();
   const ComputedStyle* area_style = snap_area.Style();
   SnapAreaData snap_area_data;
-  LayoutRect container(
-      LayoutPoint(),
-      LayoutSize(snap_container.OffsetWidth(), snap_container.OffsetHeight()));
+
+  // Scroll-padding represents inward offsets from the corresponding edge of the
+  // scrollport. https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding
+  // Scrollport is the visual vieport of the scroll container (through which the
+  // scrollable overflow region can be viewed) coincides with its padding box.
+  // https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding
+  // So we use the size of the padding box here.
+  LayoutRect container(LayoutPoint(), snap_container.PaddingBoxRect().Size());
+
   // We assume that the snap_container is the snap_area's ancestor in layout
   // tree, as the snap_container is found by walking up the layout tree in
   // FindSnapContainer(). Under this assumption,
diff --git a/third_party/WebKit/Source/core/page/scrolling/SnapCoordinatorTest.cpp b/third_party/WebKit/Source/core/page/scrolling/SnapCoordinatorTest.cpp
index 7c5bf441..8b945314 100644
--- a/third_party/WebKit/Source/core/page/scrolling/SnapCoordinatorTest.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/SnapCoordinatorTest.cpp
@@ -418,10 +418,12 @@
                             ->MaximumScrollOffset()
                             .Height();
 
-  // (#area.left + #area.right) / 2 - (#scroller.left + #scroller.right) / 2
-  double snap_offset_x = (200 + (200 + 100)) / 2 - 140 / 2;
-  // (#area.top + #area.bottom) / 2 - (#scroller.top + #scroller.bottom) / 2
-  double snap_offset_y = (200 + (200 + 100)) / 2 - 160 / 2;
+  // (#area.left + #area.right) / 2 - #scroller.width / 2
+  double snap_offset_x =
+      (200 + (200 + 100)) / 2 - float(scroller_element->clientWidth()) / 2;
+  // (#area.top + #area.bottom) / 2 - #scroller.height / 2
+  double snap_offset_y =
+      (200 + (200 + 100)) / 2 - float(scroller_element->clientHeight()) / 2;
 
   bool must_snap = false;
 
@@ -475,14 +477,16 @@
   // (#scroller.left + #scroller.scroll-padding-left +
   //  #scroller.right - #scroller.scroll-padding-right) / 2
   double snap_offset_x =
-      (200 - 8 + (200 + 100 + 4)) / 2 - (0 + 16 + 140 - 12) / 2;
+      (200 - 8 + (200 + 100 + 4)) / 2 -
+      (0 + 16 + float(scroller_element->clientWidth()) - 12) / 2;
 
   // (#area.top - #area.scroll-snap-margin-top +
   //  #area.bottom + #area.scroll-snap-margin-bottom) / 2 -
   // (#scroller.top + #scroller.scroll-padding-top +
   //  #scroller.bottom - #scroller.scroll-padding-bottom) / 2
   double snap_offset_y =
-      (200 - 2 + (200 + 100 + 6)) / 2 - (0 + 10 + 160 - 14) / 2;
+      (200 - 2 + (200 + 100 + 6)) / 2 -
+      (0 + 10 + float(scroller_element->clientHeight()) - 14) / 2;
 
   bool must_snap = false;
 
@@ -519,11 +523,13 @@
 
   // (#area.right + #area.scroll-snap-margin)
   // - (#scroller.right - #scroller.scroll-padding)
-  double snap_offset_x = (200 + 100 + 8) - (140 - 10);
+  double snap_offset_x =
+      (200 + 100 + 8) - (scroller_element->clientWidth() - 10);
 
   // (#area.bottom + #area.scroll-snap-margin)
   // - (#scroller.bottom - #scroller.scroll-padding)
-  double snap_offset_y = (200 + 100 + 8) - (160 - 10);
+  double snap_offset_y =
+      (200 + 100 + 8) - (scroller_element->clientHeight() - 10);
 
   bool must_snap = false;
 
@@ -561,14 +567,14 @@
 
   // (#area.right + #area.scroll-snap-margin)
   //  - (#scroller.right - #scroller.scroll-padding)
-  // = (100 + 8) - (140 - 10)
-  // As scrollOffset cannot be set to -22, we set it to 0.
+  // = (100 + 8) - (clientWidth - 10) < 0
+  // As scrollOffset cannot be set to a negative number, we set it to 0.
   double snap_offset_x = 0;
 
   // (#area.bottom + #area.scroll-snap-margin)
   //  - (#scroller.bottom - #scroller.scroll-padding)
-  // = (100 + 8) - (160 - 10)
-  // As scrollOffset cannot be set to -42, we set it to 0.
+  // = (100 + 8) - (clientHeight - 10) < 0
+  // As scrollOffset cannot be set to a negative number, we set it to 0.
   double snap_offset_y = 0;
 
   bool must_snap = false;
diff --git a/third_party/WebKit/Source/platform/OWNERS b/third_party/WebKit/Source/platform/OWNERS
index f4397b3..3785f08 100644
--- a/third_party/WebKit/Source/platform/OWNERS
+++ b/third_party/WebKit/Source/platform/OWNERS
@@ -1,4 +1,3 @@
-dglazkov@chromium.org
 drott@chromium.org
 eae@chromium.org
 fmalita@chromium.org
diff --git a/third_party/WebKit/public/OWNERS b/third_party/WebKit/public/OWNERS
index 08f313b..69861f5 100644
--- a/third_party/WebKit/public/OWNERS
+++ b/third_party/WebKit/public/OWNERS
@@ -1,6 +1,5 @@
 chrishtr@chromium.org
 dcheng@chromium.org
-dglazkov@chromium.org
 dstockwell@chromium.org
 foolip@chromium.org
 haraken@chromium.org
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ec085780..b17e65b1 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -25030,6 +25030,7 @@
   <int value="-790036192" label="overscroll-start-threshold"/>
   <int value="-787426248" label="ChromeHomeSurvey:disabled"/>
   <int value="-780798969" label="disable-single-click-autofill"/>
+  <int value="-780599934" label="AutofillUpstreamSendPanFirstSix:enabled"/>
   <int value="-778126349" label="DownloadsLocationChange:enabled"/>
   <int value="-776686417" label="SiteExplorationUi:disabled"/>
   <int value="-775321548" label="UseNewDoodleApi:disabled"/>
@@ -25296,6 +25297,7 @@
   <int value="-29847483" label="MemoryAblation:enabled"/>
   <int value="-23090520" label="disable-search-button-in-omnibox"/>
   <int value="-22544408" label="enable-video-player-chromecast-support"/>
+  <int value="-19354048" label="AutofillUpstreamSendPanFirstSix:disabled"/>
   <int value="-16824589" label="ash-shelf-color"/>
   <int value="-13918890" label="disable-download-notification"/>
   <int value="-11260186" label="enable-offline-pages-as-saved-pages"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 4941789..d2401d3 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -13518,15 +13518,6 @@
   </summary>
 </histogram>
 
-<histogram name="DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch"
-    enum="BooleanSuccess">
-  <owner>tbansal@chromium.org</owner>
-  <summary>
-    Whether the warmup (or probe) URL was successfully fetched over a data saver
-    proxy.
-  </summary>
-</histogram>
-
 <histogram name="DataUsage.MatchingRulesCount.Invalid" units="count">
   <owner>bengr@chromium.org</owner>
   <owner>rajendrant@chromium.org</owner>
@@ -101144,20 +101135,6 @@
   <affected-histogram name="Previews.OriginalContentLength"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="DataSaverProxyTypes" separator=".">
-  <owner>tbansal@chromium.org</owner>
-  <suffix name="SecureProxy.Core"
-      label="Over a secure, core data saver proxy."/>
-  <suffix name="SecureProxy.NonCore"
-      label="Over a secure, non-core data saver proxy."/>
-  <suffix name="InsecureProxy.Core"
-      label="Over an insecure, core data saver proxy."/>
-  <suffix name="InsecureProxy.NonCore"
-      label="Over an insecure, non-core data saver proxy."/>
-  <affected-histogram
-      name="DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="DataUsageReportSubmissionBytes" separator=".">
   <suffix name="Failed"
       label="Platform external data use observer reported the submission as
diff --git a/ui/accessibility/platform/atk_util_auralinux_gtk2.cc b/ui/accessibility/platform/atk_util_auralinux_gtk2.cc
index 9c50ead8..ac11b56 100644
--- a/ui/accessibility/platform/atk_util_auralinux_gtk2.cc
+++ b/ui/accessibility/platform/atk_util_auralinux_gtk2.cc
@@ -20,6 +20,8 @@
 const char kAtkBridgeSymbolName[] = "gnome_accessibility_module_init";
 const char kGtkModules[] = "GTK_MODULES";
 
+namespace ui {
+
 // Returns a function pointer to be invoked on the main thread to init
 // the gnome accessibility module if it's enabled (nullptr otherwise).
 GnomeAccessibilityModuleInitFunc GetAccessibilityModuleInitFunc() {
@@ -78,3 +80,5 @@
       base::Bind(&GetAccessibilityModuleInitFunc),
       base::Bind(&FinishAccessibilityInitOnMainThread));
 }
+
+}  // namespace ui
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 04840b9f..3645718 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -288,6 +288,8 @@
     context_factory_private_->SetDisplayVisible(this, host_->IsVisible());
     context_factory_private_->SetDisplayColorSpace(this, blending_color_space_,
                                                    output_color_space_);
+    context_factory_private_->SetDisplayColorMatrix(this,
+                                                    display_color_matrix_);
   }
 }
 
@@ -315,6 +317,12 @@
   return animation_timeline_.get();
 }
 
+void Compositor::SetDisplayColorMatrix(const SkMatrix44& matrix) {
+  display_color_matrix_ = matrix;
+  if (context_factory_private_)
+    context_factory_private_->SetDisplayColorMatrix(this, matrix);
+}
+
 void Compositor::ScheduleFullRedraw() {
   // TODO(enne): Some callers (mac) call this function expecting that it
   // will also commit.  This should probably just redraw the screen
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index 981727c5..5e54899a 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -25,6 +25,7 @@
 #include "components/viz/common/surfaces/surface_sequence.h"
 #include "components/viz/host/host_frame_sink_client.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkMatrix44.h"
 #include "ui/compositor/compositor_animation_observer.h"
 #include "ui/compositor/compositor_export.h"
 #include "ui/compositor/compositor_lock.h"
@@ -124,6 +125,11 @@
   virtual void ResizeDisplay(ui::Compositor* compositor,
                              const gfx::Size& size) = 0;
 
+  // Sets the color matrix used to transform how all output is drawn to the
+  // display underlying this ui::Compositor.
+  virtual void SetDisplayColorMatrix(ui::Compositor* compositor,
+                                     const SkMatrix44& matrix) = 0;
+
   // Set the output color profile into which this compositor should render.
   virtual void SetDisplayColorSpace(
       ui::Compositor* compositor,
@@ -239,6 +245,13 @@
     return output_color_space_;
   }
 
+  // Gets and sets the color matrix used to transform the output colors of what
+  // this compositor renders.
+  const SkMatrix44& display_color_matrix() const {
+    return display_color_matrix_;
+  }
+  void SetDisplayColorMatrix(const SkMatrix44& matrix);
+
   // Where possible, draws are scissored to a damage region calculated from
   // changes to layer properties.  This bypasses that and indicates that
   // the whole frame needs to be drawn.
@@ -479,6 +492,8 @@
   scoped_refptr<cc::AnimationTimeline> animation_timeline_;
   std::unique_ptr<ScopedAnimationDurationScaleMode> slow_animations_;
 
+  SkMatrix44 display_color_matrix_;
+
   gfx::ColorSpace output_color_space_;
   gfx::ColorSpace blending_color_space_;
 
diff --git a/ui/compositor/compositor_unittest.cc b/ui/compositor/compositor_unittest.cc
index 08ce8b9..eccd07a 100644
--- a/ui/compositor/compositor_unittest.cc
+++ b/ui/compositor/compositor_unittest.cc
@@ -19,6 +19,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/test/context_factories_for_test.h"
 #include "ui/compositor/test/draw_waiter_for_test.h"
+#include "ui/compositor/test/in_process_context_factory.h"
 
 using testing::Mock;
 using testing::_;
@@ -124,6 +125,43 @@
 
 }  // namespace
 
+TEST_F(CompositorTestWithMessageLoop, OutputColorMatrix) {
+  auto root_layer = std::make_unique<Layer>(ui::LAYER_SOLID_COLOR);
+  root_layer->SetBounds(gfx::Rect(10, 10));
+  compositor()->SetRootLayer(root_layer.get());
+  compositor()->SetScaleAndSize(1.0f, gfx::Size(10, 10));
+  DCHECK(compositor()->IsVisible());
+
+  // Set a non-identity color matrix on the compistor display, and expect it to
+  // be set on the context factory.
+  SkMatrix44 color_matrix(SkMatrix44::kIdentity_Constructor);
+  color_matrix.set(1, 1, 0.7f);
+  color_matrix.set(2, 2, 0.4f);
+  compositor()->SetDisplayColorMatrix(color_matrix);
+  InProcessContextFactory* context_factory_private =
+      static_cast<InProcessContextFactory*>(
+          compositor()->context_factory_private());
+  compositor()->ScheduleDraw();
+  DrawWaiterForTest::WaitForCompositingEnded(compositor());
+  EXPECT_EQ(color_matrix,
+            context_factory_private->GetOutputColorMatrix(compositor()));
+
+  // Simulate a lost context by releasing the output surface and setting it on
+  // the compositor again. Expect that the same color matrix will be set again
+  // on the context factory.
+  context_factory_private->ResetOutputColorMatrixToIdentity(compositor());
+  compositor()->SetVisible(false);
+  EXPECT_EQ(gfx::kNullAcceleratedWidget,
+            compositor()->ReleaseAcceleratedWidget());
+  compositor()->SetAcceleratedWidget(gfx::kNullAcceleratedWidget);
+  compositor()->SetVisible(true);
+  compositor()->ScheduleDraw();
+  DrawWaiterForTest::WaitForCompositingEnded(compositor());
+  EXPECT_EQ(color_matrix,
+            context_factory_private->GetOutputColorMatrix(compositor()));
+  compositor()->SetRootLayer(nullptr);
+}
+
 TEST_F(CompositorTestWithMockedTime, LocksAreObserved) {
   std::unique_ptr<CompositorLock> lock;
 
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index b6cd30de..b9c5d1bd 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -147,6 +147,7 @@
   gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
   std::unique_ptr<viz::BeginFrameSource> begin_frame_source;
   std::unique_ptr<viz::Display> display;
+  SkMatrix44 output_color_matrix;
 };
 
 InProcessContextFactory::InProcessContextFactory(
@@ -352,6 +353,16 @@
   per_compositor_data_[compositor]->display->Resize(size);
 }
 
+void InProcessContextFactory::SetDisplayColorMatrix(ui::Compositor* compositor,
+                                                    const SkMatrix44& matrix) {
+  auto iter = per_compositor_data_.find(compositor);
+  if (iter == per_compositor_data_.end())
+    return;
+
+  iter->second->output_color_matrix = matrix;
+  iter->second->display->SetColorMatrix(matrix);
+}
+
 const viz::ResourceSettings& InProcessContextFactory::GetResourceSettings()
     const {
   return renderer_settings_.resource_settings;
@@ -369,6 +380,24 @@
   return frame_sink_manager_;
 }
 
+SkMatrix44 InProcessContextFactory::GetOutputColorMatrix(
+    Compositor* compositor) const {
+  auto iter = per_compositor_data_.find(compositor);
+  if (iter == per_compositor_data_.end())
+    return SkMatrix44(SkMatrix44::kIdentity_Constructor);
+
+  return iter->second->output_color_matrix;
+}
+
+void InProcessContextFactory::ResetOutputColorMatrixToIdentity(
+    ui::Compositor* compositor) {
+  auto iter = per_compositor_data_.find(compositor);
+  if (iter == per_compositor_data_.end())
+    return;
+
+  iter->second->output_color_matrix.setIdentity();
+}
+
 InProcessContextFactory::PerCompositorData*
 InProcessContextFactory::CreatePerCompositorData(ui::Compositor* compositor) {
   DCHECK(!per_compositor_data_[compositor]);
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index e059fd9..85cd49ca 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -74,6 +74,8 @@
   void SetDisplayVisible(ui::Compositor* compositor, bool visible) override;
   void ResizeDisplay(ui::Compositor* compositor,
                      const gfx::Size& size) override;
+  void SetDisplayColorMatrix(ui::Compositor* compositor,
+                             const SkMatrix44& matrix) override;
   void SetDisplayColorSpace(
       ui::Compositor* compositor,
       const gfx::ColorSpace& blending_color_space,
@@ -91,6 +93,9 @@
   void RemoveObserver(ContextFactoryObserver* observer) override;
   viz::FrameSinkManagerImpl* GetFrameSinkManager() override;
 
+  SkMatrix44 GetOutputColorMatrix(Compositor* compositor) const;
+  void ResetOutputColorMatrixToIdentity(ui::Compositor* compositor);
+
  private:
   struct PerCompositorData;
 
diff --git a/ui/message_center/views/message_view_context_menu_controller.cc b/ui/message_center/views/message_view_context_menu_controller.cc
index fb871b6..6d363c4 100644
--- a/ui/message_center/views/message_view_context_menu_controller.cc
+++ b/ui/message_center/views/message_view_context_menu_controller.cc
@@ -33,13 +33,10 @@
   if (!menu_model_ || menu_model_->GetItemCount() == 0)
     return;
 
-  menu_model_adapter_.reset(new views::MenuModelAdapter(
-      menu_model_.get(),
+  menu_runner_ = std::make_unique<views::MenuRunner>(
+      menu_model_.get(), views::MenuRunner::HAS_MNEMONICS,
       base::Bind(&MessageViewContextMenuController::OnMenuClosed,
-                 base::Unretained(this))));
-
-  menu_runner_.reset(new views::MenuRunner(menu_model_adapter_->CreateMenu(),
-                                           views::MenuRunner::HAS_MNEMONICS));
+                 base::Unretained(this)));
 
   menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(), NULL,
                           gfx::Rect(point, gfx::Size()),
@@ -48,7 +45,6 @@
 
 void MessageViewContextMenuController::OnMenuClosed() {
   menu_runner_.reset();
-  menu_model_adapter_.reset();
   menu_model_.reset();
 }
 
diff --git a/ui/message_center/views/message_view_context_menu_controller.h b/ui/message_center/views/message_view_context_menu_controller.h
index 49596008..8ee27f0 100644
--- a/ui/message_center/views/message_view_context_menu_controller.h
+++ b/ui/message_center/views/message_view_context_menu_controller.h
@@ -17,7 +17,6 @@
 }  // namespace ui
 
 namespace views {
-class MenuModelAdapter;
 class MenuRunner;
 }  // namespace views
 
@@ -35,11 +34,10 @@
                               const gfx::Point& point,
                               ui::MenuSourceType source_type) override;
 
-  // Callback for MenuModelAdapter
+  // Callback for MenuRunner
   void OnMenuClosed();
 
   std::unique_ptr<ui::MenuModel> menu_model_;
-  std::unique_ptr<views::MenuModelAdapter> menu_model_adapter_;
   std::unique_ptr<views::MenuRunner> menu_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(MessageViewContextMenuController);