diff --git a/DEPS b/DEPS
index aef4e43..f8c364a 100644
--- a/DEPS
+++ b/DEPS
@@ -138,11 +138,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '62fd2c32bbe637b15e9dbc4b3c15ebd3291597ea',
+  'skia_revision': 'ac79ca16c651f020a4c257d75919b00eca61b494',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '9672bf6d0b325ae9a9157ca217db965bca7e76f9',
+  'v8_revision': '10dea84f8dfc007114fa953fb00cb721afd98e69',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -154,7 +154,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '75841d73c2de727355d48d8d9af9f6cd7e3f7738',
+  'swiftshader_revision': '5b424e69101a103a630327b8de7a6636dfddea2c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -201,7 +201,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'f8aef9991ba929e8e7c202fbcebccce376ffc726',
+  'catapult_revision': 'dd7a2ceeb49ed46f23f9048b0b7fbecfb84461db',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -832,7 +832,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0f476788122d4b40b0293226e193e534ec66cad6',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '71c6bc07e67f3a09b9e03d672a154c3b638103a2',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -901,7 +901,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'a549bb81752365526f6d7334f00961ea08689211',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '2f4a8dfd3a596d75e3c26cb7ae9b68886d3a19cf',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1164,7 +1164,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '6f26bce0b1c4e8ce0e13332f7c0083788def5fdf',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'efc3e5ad59d5f30529b3d1df91e6688d0bb0b80c',
+    Var('chromium_git') + '/openscreen' + '@' + 'd384da5a955f7594615ae49b5f624cb135d0cb01',
 
   'src/third_party/ow2_asm': {
       'packages': [
@@ -1396,7 +1396,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@009bc1a74cf5241077059f06969413d9e5ba4d32',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@002d0e113a3bba7a05d2c0205a90231b5d7a025f',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index afa120f..dd675039 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -767,11 +767,6 @@
                   'extensions/browser/api/networking_private/|'\
                   'ui/chromeos/network/',
     },
-    'chromeos_power': {
-      'filepath': 'ash/system/power/|'\
-                  'chromeos/dbus/power_.*|'\
-                  'chrome/browser/chromeos/power/',
-    },
     'chromeos_timezone': {
       'filepath': 'chromeos/timezone/',
     },
@@ -1111,11 +1106,6 @@
       'filepath': 'third_party/libxml/' \
                   '|third_party/libxslt/',
     },
-    'linux_fonts': {
-      'filepath': 'ui/gfx/font_render_params_.*|'\
-                  'ui/gfx/platform_font_linux.*|'\
-                  'ui/gfx/render_text_harfbuzz.*',
-    },
     'linux_sandboxing': {
       'filepath': 'sandbox/linux/',
     },
@@ -1886,8 +1876,7 @@
   'WATCHLISTS': {
     'about_flags': ['asvitkine+watch@chromium.org',
                     'jmedley+watch@chromium.org'],
-    'accelerator_table': ['derat+watch@chromium.org',
-                          'yusukes+watch@chromium.org'],
+    'accelerator_table': ['yusukes+watch@chromium.org'],
     'accessibility': ['aboxhall+watch@chromium.org',
                       'akihiroota@chromium.org',
                       'aleventhal+watch@chromium.org',
@@ -2221,7 +2210,6 @@
                      'jlklein+watch-network@chromium.org',
                      'khorimoto+watch-network@chromium.org',
                      'stevenjb+watch-network@chromium.org'],
-    'chromeos_power': ['derat+watch@chromium.org'],
     'chromeos_timezone': ['alemate+watch@chromium.org'],
     'chromeos_webui': ['alemate+watch@chromium.org'],
     'chromevox': ['anastasi+watch@google.com'],
@@ -2387,7 +2375,6 @@
                 'urvang@chromium.org'],
     'libxml_xslt': ['ail@google.com',
                     'dominicc+watchlist@chromium.org'],
-    'linux_fonts': ['derat+watch@chromium.org'],
     'linux_sandboxing': ['jln+watch@chromium.org'],
     'linux_seccomp_bpf': ['jln+watch@chromium.org'],
     'mac' : ['mac-reviews@chromium.org'],
@@ -2665,8 +2652,7 @@
                          'msramek+watch@chromium.org'],
     'webui_backend': ['dbeam+watch-webui-backend@chromium.org'],
     'windows_sandboxing': ['wfh+watch@chromium.org'],
-    'x11': ['derat+watch@chromium.org',
-            'sadrul@chromium.org',
+    'x11': ['sadrul@chromium.org',
             'yusukes+watch@chromium.org'],
     'zlib': ['cblume+zlib@chromium.org'],
     'zoom': ['wjmaclean@chromium.org'],
diff --git a/android_webview/browser/aw_ssl_host_state_delegate.cc b/android_webview/browser/aw_ssl_host_state_delegate.cc
index 56f8ebf..a8be0ae 100644
--- a/android_webview/browser/aw_ssl_host_state_delegate.cc
+++ b/android_webview/browser/aw_ssl_host_state_delegate.cc
@@ -56,7 +56,7 @@
 bool AwSSLHostStateDelegate::DidHostRunInsecureContent(
     const std::string& host,
     int child_id,
-    InsecureContentType content_type) const {
+    InsecureContentType content_type) {
   // Intentional no-op for Android WebView.
   return false;
 }
@@ -100,7 +100,7 @@
   cert_policy_for_host_.erase(host);
 }
 
-bool AwSSLHostStateDelegate::HasAllowException(const std::string& host) const {
+bool AwSSLHostStateDelegate::HasAllowException(const std::string& host) {
   auto policy_iterator = cert_policy_for_host_.find(host);
   return policy_iterator != cert_policy_for_host_.end() &&
          policy_iterator->second.HasAllowException();
diff --git a/android_webview/browser/aw_ssl_host_state_delegate.h b/android_webview/browser/aw_ssl_host_state_delegate.h
index 79f9bb3..9776ebb7 100644
--- a/android_webview/browser/aw_ssl_host_state_delegate.h
+++ b/android_webview/browser/aw_ssl_host_state_delegate.h
@@ -68,10 +68,9 @@
                               InsecureContentType content_type) override;
 
   // Returns whether the specified host ran insecure content.
-  bool DidHostRunInsecureContent(
-      const std::string& host,
-      int child_id,
-      InsecureContentType content_type) const override;
+  bool DidHostRunInsecureContent(const std::string& host,
+                                 int child_id,
+                                 InsecureContentType content_type) override;
 
   // Revokes all SSL certificate error allow exceptions made by the user for
   // |host|.
@@ -81,7 +80,7 @@
   // |host|. This does not mean that *all* certificate errors are allowed, just
   // that there exists an exception. To see if a particular certificate and
   // error combination exception is allowed, use QueryPolicy().
-  bool HasAllowException(const std::string& host) const override;
+  bool HasAllowException(const std::string& host) override;
 
  private:
   // Certificate policies for each host.
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index cfdb79ad..de05149d 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.android_webview.test;
 
+import android.support.annotation.IntDef;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
@@ -30,9 +31,10 @@
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.net.test.util.TestWebServer;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -45,6 +47,24 @@
     @Rule
     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
 
+    @IntDef({CookieLifetime.OUTLIVE_THE_TEST_SEC, CookieLifetime.EXPIRE_DURING_TEST_SEC,
+            CookieLifetime.ALREADY_EXPIRED_SEC})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface CookieLifetime {
+        /** Longer than the limit of tests, so cookies will not expire during the test. */
+        final int OUTLIVE_THE_TEST_SEC = 10 * 60; // 10 minutes
+
+        /**
+         * Shorter than the limit of tests, so cookies may expire during the test. Be sure to wait
+         * at least this duration after <b>setting</b> the cookie (ex. via {@link
+         * AwCookieManager#setCookie(String)}).
+         */
+        final int EXPIRE_DURING_TEST_SEC = 1;
+
+        /** Guarantees the cookie is expired, immediately when set. */
+        final int ALREADY_EXPIRED_SEC = -1;
+    }
+
     private AwCookieManager mCookieManager;
     private TestAwContentsClient mContentsClient;
     private AwContents mAwContents;
@@ -271,7 +291,8 @@
         final String normalCookie = "cookie2=sue";
 
         mCookieManager.setCookie(url, sessionCookie);
-        mCookieManager.setCookie(url, makeExpiringCookie(normalCookie, 600));
+        mCookieManager.setCookie(
+                url, makeExpiringCookie(normalCookie, CookieLifetime.OUTLIVE_THE_TEST_SEC));
 
         mCookieManager.removeSessionCookies();
 
@@ -444,7 +465,8 @@
         int callCount = callback.getOnResultHelper().getCallCount();
 
         mCookieManager.setCookie(url, sessionCookie);
-        mCookieManager.setCookie(url, makeExpiringCookie(normalCookie, 600));
+        mCookieManager.setCookie(
+                url, makeExpiringCookie(normalCookie, CookieLifetime.OUTLIVE_THE_TEST_SEC));
 
         // When there is a session cookie then it is removed.
         removeSessionCookiesOnUiThread(callback);
@@ -471,7 +493,8 @@
         final String normalCookie = "cookie2=sue";
 
         mCookieManager.setCookie(url, sessionCookie);
-        mCookieManager.setCookie(url, makeExpiringCookie(normalCookie, 600));
+        mCookieManager.setCookie(
+                url, makeExpiringCookie(normalCookie, CookieLifetime.OUTLIVE_THE_TEST_SEC));
         String allCookies = mCookieManager.getCookie(url);
         Assert.assertTrue(allCookies.contains(sessionCookie));
         Assert.assertTrue(allCookies.contains(normalCookie));
@@ -492,7 +515,8 @@
         final String url = "http://www.example.com";
         final String cookie = "cookie1=peter";
 
-        mCookieManager.setCookie(url, makeExpiringCookie(cookie, -1));
+        mCookieManager.setCookie(
+                url, makeExpiringCookie(cookie, CookieLifetime.ALREADY_EXPIRED_SEC));
         assertNoCookies(url);
     }
 
@@ -503,10 +527,10 @@
         final String url = "http://www.example.com";
         final String cookie = "cookie1=peter";
 
-        mCookieManager.setCookie(url, makeExpiringCookieMs(cookie, 1200));
+        mCookieManager.setCookie(
+                url, makeExpiringCookie(cookie, CookieLifetime.EXPIRE_DURING_TEST_SEC));
 
-        // The cookie exists:
-        Assert.assertTrue(mCookieManager.hasCookies());
+        Assert.assertTrue("Cookie should exist before expiration", mCookieManager.hasCookies());
 
         // But eventually expires:
         AwActivityTestRule.pollInstrumentationThread(() -> !mCookieManager.hasCookies());
@@ -521,7 +545,8 @@
         final String longCookie = "cookie2=marc";
 
         mCookieManager.setCookie(url, sessionCookie);
-        mCookieManager.setCookie(url, makeExpiringCookie(longCookie, 600));
+        mCookieManager.setCookie(
+                url, makeExpiringCookie(longCookie, CookieLifetime.OUTLIVE_THE_TEST_SEC));
 
         String allCookies = mCookieManager.getCookie(url);
         Assert.assertTrue(allCookies.contains(sessionCookie));
@@ -1130,15 +1155,17 @@
                 foundCookieNamesSet);
     }
 
-    private String makeExpiringCookie(String cookie, int secondsTillExpiry) {
-        return makeExpiringCookieMs(cookie, secondsTillExpiry * 1000);
-    }
-
+    /**
+     * Makes a cookie which expires {@code secondsTillExpiry} seconds after the cookie is set. Note:
+     * cookie expiration can only be specified to a precisiion of seconds, not to the millisecond.
+     * See https://tools.ietf.org/html/rfc6265#section-4.1 and
+     * https://tools.ietf.org/html/rfc7231#section-7.1.1.2 for details.
+     */
     @SuppressWarnings("deprecation")
-    private String makeExpiringCookieMs(String cookie, int millisecondsTillExpiry) {
-        Date date = new Date();
-        date.setTime(date.getTime() + millisecondsTillExpiry);
-        return cookie + "; expires=" + date.toGMTString();
+    private String makeExpiringCookie(String cookie, @CookieLifetime int secondsTillExpiry) {
+        // Use "Max-Age" instead of "Expires", since "Max-Age" is relative to the time the cookie is
+        // set, rather than a call to the Date constructor when building this cookie string.
+        return cookie + "; Max-Age=" + secondsTillExpiry;
     }
 
     /**
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 9b668e2..f0eb7fff 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1018,8 +1018,6 @@
     "utility/screenshot_controller.h",
     "utility/transformer_util.cc",
     "utility/transformer_util.h",
-    "voice_interaction/voice_interaction_controller.cc",
-    "voice_interaction/voice_interaction_controller.h",
     "wallpaper/wallpaper_base_view.cc",
     "wallpaper/wallpaper_base_view.h",
     "wallpaper/wallpaper_controller_impl.cc",
@@ -1808,7 +1806,6 @@
     "tray_action/test_tray_action_client.h",
     "tray_action/tray_action_unittest.cc",
     "utility/screenshot_controller_unittest.cc",
-    "voice_interaction/voice_interaction_controller_unittest.cc",
     "wallpaper/wallpaper_controller_unittest.cc",
     "wallpaper/wallpaper_utils/wallpaper_color_calculator_unittest.cc",
     "wallpaper/wallpaper_utils/wallpaper_resizer_unittest.cc",
diff --git a/ash/OWNERS b/ash/OWNERS
index 623de27..f785a7d 100644
--- a/ash/OWNERS
+++ b/ash/OWNERS
@@ -1,5 +1,4 @@
 afakhry@chromium.org
-derat@chromium.org
 jamescook@chromium.org
 oshima@chromium.org
 sky@chromium.org
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index 46d4994..f1f5749 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -35,6 +35,7 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/new_window_delegate.h"
 #include "ash/public/cpp/notification_utils.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/public/interfaces/accessibility_controller.mojom.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/root_window_controller.h"
@@ -59,7 +60,6 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/touch/touch_hud_debug.h"
 #include "ash/utility/screenshot_controller.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/screen_pinning_controller.h"
@@ -695,9 +695,8 @@
         base::UserMetricsAction("VoiceInteraction.Started.Assistant"));
   }
 
-  switch (
-      Shell::Get()->voice_interaction_controller()->allowed_state().value_or(
-          mojom::AssistantAllowedState::ALLOWED)) {
+  switch (VoiceInteractionController::Get()->allowed_state().value_or(
+      mojom::AssistantAllowedState::ALLOWED)) {
     case mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER:
       // Show a toast if the active user is not primary.
       ShowToast(kVoiceInteractionErrorToastId,
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index c88c82b..ac953a9 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -30,11 +30,11 @@
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -141,7 +141,7 @@
   shell->AddShellObserver(this);
   shell->overview_controller()->AddObserver(this);
   keyboard::KeyboardController::Get()->AddObserver(this);
-  shell->voice_interaction_controller()->AddLocalObserver(this);
+  VoiceInteractionController::Get()->AddLocalObserver(this);
   shell->window_tree_host_manager()->AddObserver(this);
   shell->mru_window_tracker()->AddObserver(this);
   if (app_list_features::IsEmbeddedAssistantUIEnabled()) {
@@ -1007,6 +1007,7 @@
   }
 
   CloseAssistantUi(AssistantExitPoint::kLauncherClose);
+  model_->SetState(AppListState::kInvalidState);
   if (client_)
     client_->ViewClosing();
 }
@@ -1124,7 +1125,7 @@
   if (!Shell::Get()->assistant_controller()->IsAssistantReady())
     return false;
 
-  auto* controller = Shell::Get()->voice_interaction_controller();
+  auto* controller = VoiceInteractionController::Get();
   return controller->settings_enabled().value_or(false) &&
          controller->allowed_state() == mojom::AssistantAllowedState::ALLOWED &&
          controller->voice_interaction_state().value_or(
@@ -1382,7 +1383,7 @@
   }
   shell->mru_window_tracker()->RemoveObserver(this);
   shell->window_tree_host_manager()->RemoveObserver(this);
-  shell->voice_interaction_controller()->RemoveLocalObserver(this);
+  VoiceInteractionController::Get()->RemoveLocalObserver(this);
   keyboard::KeyboardController::Get()->RemoveObserver(this);
   shell->overview_controller()->RemoveObserver(this);
   shell->RemoveShellObserver(this);
diff --git a/ash/app_list/app_list_metrics.cc b/ash/app_list/app_list_metrics.cc
index 3e29c135..a4c008aa 100644
--- a/ash/app_list/app_list_metrics.cc
+++ b/ash/app_list/app_list_metrics.cc
@@ -61,12 +61,12 @@
 // The UMA hisotogram that logs the action user performs on zero state
 // search result.
 constexpr char kAppListZeroStateSearchResultUserActionHistogram[] =
-    "Apps.AppListZeroStateSearchResultUserActionType";
+    "Apps.AppList.ZeroStateSearchResultUserActionType";
 
 // The UMA histogram that logs user's decision(remove or cancel) for zero state
 // search result removal confirmation.
 constexpr char kAppListZeroStateSearchResultRemovalHistogram[] =
-    "Apps.ZeroStateSearchResutRemovalDecision";
+    "Apps.AppList.ZeroStateSearchResultRemovalDecision";
 
 // The UMA histogram that logs the length of the query when user abandons
 // results of a queried search or recommendations of zero state(zero length
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 9c01876..f3a9b667 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -485,6 +485,9 @@
       <message name="IDS_ASH_AUTOCLICK_OPTION_DRAG_AND_DROP" desc="The tooltip text for a menu option for the automatic clicks menu that results in the next automatic click being a click and drag action.">
         Click and drag
       </message>
+      <message name="IDS_ASH_AUTOCLICK_OPTION_SCROLL" desc="The tooltip text for a menu option for the automatic clicks menu that launches a scroll pad for doing mousewheel scroll events.">
+        Scroll
+      </message>
       <message name="IDS_ASH_AUTOCLICK_OPTION_NO_ACTION" desc="The tooltip text for a menu option for the automatic clicks menu that results in pausing the automatic clicks feature without disabling it.">
         No action (pause)
       </message>
diff --git a/ash/assistant/assistant_cache_controller.cc b/ash/assistant/assistant_cache_controller.cc
index 5b02ff7..ccc8b0e 100644
--- a/ash/assistant/assistant_cache_controller.cc
+++ b/ash/assistant/assistant_cache_controller.cc
@@ -11,9 +11,9 @@
 #include "ash/assistant/assistant_ui_controller.h"
 #include "ash/assistant/util/assistant_util.h"
 #include "ash/assistant/util/deep_link_util.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/rand_util.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
@@ -66,12 +66,12 @@
     : assistant_controller_(assistant_controller) {
   UpdateConversationStarters();
   assistant_controller_->AddObserver(this);
-  Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
+  VoiceInteractionController::Get()->AddLocalObserver(this);
 }
 
 AssistantCacheController::~AssistantCacheController() {
   assistant_controller_->RemoveObserver(this);
-  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
+  VoiceInteractionController::Get()->RemoveLocalObserver(this);
 }
 
 void AssistantCacheController::AddModelObserver(
@@ -132,8 +132,7 @@
 
   // If enabled, always show the "What's on my screen?" conversation starter.
   if (kWhatsOnMyScreenChipEnabled.Get() &&
-      Shell::Get()->voice_interaction_controller()->context_enabled().value_or(
-          false)) {
+      VoiceInteractionController::Get()->context_enabled().value_or(false)) {
     AddConversationStarter(IDS_ASH_ASSISTANT_CHIP_WHATS_ON_MY_SCREEN,
                            assistant::util::CreateWhatsOnMyScreenDeepLink());
   }
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index 9621c098..436d1bc 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -12,10 +12,10 @@
 #include "ash/public/cpp/android_intent_helper.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/new_window_delegate.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/utility/screenshot_controller.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/bind.h"
 #include "base/memory/scoped_refptr.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -42,7 +42,7 @@
       assistant_ui_controller_(this),
       view_delegate_(this),
       weak_factory_(this) {
-  Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
+  VoiceInteractionController::Get()->AddLocalObserver(this);
   chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
   AddObserver(this);
 
@@ -54,7 +54,7 @@
 
   chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this);
   Shell::Get()->accessibility_controller()->RemoveObserver(this);
-  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
+  VoiceInteractionController::Get()->RemoveLocalObserver(this);
   RemoveObserver(this);
 }
 
@@ -118,7 +118,7 @@
 
 void AssistantController::StartSpeakerIdEnrollmentFlow() {
   mojom::ConsentStatus consent_status =
-      Shell::Get()->voice_interaction_controller()->consent_status().value_or(
+      VoiceInteractionController::Get()->consent_status().value_or(
           mojom::ConsentStatus::kUnknown);
   if (consent_status == mojom::ConsentStatus::kActivityControlAccepted) {
     // If activity control has been accepted, launch the enrollment flow.
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index 57c2ba4..0a136a4 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -21,11 +21,11 @@
 #include "ash/assistant/util/histogram_util.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/bind.h"
 #include "base/optional.h"
@@ -694,7 +694,7 @@
             assistant_controller_->ui_controller()->model()->visibility());
 
   const bool launch_with_mic_open =
-      Shell::Get()->voice_interaction_controller()->launch_with_mic_open();
+      VoiceInteractionController::Get()->launch_with_mic_open();
   const bool prefer_voice = launch_with_mic_open || IsTabletMode();
 
   // We don't explicitly start a new voice interaction if the entry point
diff --git a/ash/assistant/assistant_notification_controller.cc b/ash/assistant/assistant_notification_controller.cc
index 19dcc7a..c837890 100644
--- a/ash/assistant/assistant_notification_controller.cc
+++ b/ash/assistant/assistant_notification_controller.cc
@@ -11,10 +11,10 @@
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/vector_icons/vector_icons.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/message_center.h"
@@ -200,7 +200,7 @@
 void AssistantNotificationController::OnNotificationAdded(
     const AssistantNotification* notification) {
   // Do not show system notifications if the setting is disabled.
-  if (!Shell::Get()->voice_interaction_controller()->notification_enabled())
+  if (!VoiceInteractionController::Get()->notification_enabled())
     return;
 
   // We only show system notifications in the Message Center.
@@ -214,7 +214,7 @@
 void AssistantNotificationController::OnNotificationUpdated(
     const AssistantNotification* notification) {
   // Do not show system notifications if the setting is disabled.
-  if (!Shell::Get()->voice_interaction_controller()->notification_enabled())
+  if (!VoiceInteractionController::Get()->notification_enabled())
     return;
 
   // If the notification that was updated is *not* a system notification, we
diff --git a/ash/assistant/assistant_screen_context_controller.cc b/ash/assistant/assistant_screen_context_controller.cc
index bbc4bcf7..7d889e65 100644
--- a/ash/assistant/assistant_screen_context_controller.cc
+++ b/ash/assistant/assistant_screen_context_controller.cc
@@ -12,9 +12,7 @@
 #include "ash/assistant/assistant_ui_controller.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
-#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/shell.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "base/bind.h"
 #include "base/memory/scoped_refptr.h"
diff --git a/ash/assistant/assistant_setup_controller.cc b/ash/assistant/assistant_setup_controller.cc
index 8956dd4e..2d5caf0 100644
--- a/ash/assistant/assistant_setup_controller.cc
+++ b/ash/assistant/assistant_setup_controller.cc
@@ -8,9 +8,9 @@
 #include "ash/assistant/assistant_ui_controller.h"
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/assistant/util/i18n_util.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/shell.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "chromeos/services/assistant/public/features.h"
@@ -58,7 +58,7 @@
 
 void AssistantSetupController::OnOptInButtonPressed() {
   mojom::ConsentStatus consent_status =
-      Shell::Get()->voice_interaction_controller()->consent_status().value_or(
+      VoiceInteractionController::Get()->consent_status().value_or(
           mojom::ConsentStatus::kUnknown);
 
   if (consent_status == mojom::ConsentStatus::kUnauthorized) {
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index 207cca6c..dbf3641 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -14,12 +14,12 @@
 #include "ash/assistant/util/histogram_util.h"
 #include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/toast/toast_data.h"
 #include "ash/system/toast/toast_manager.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/bind.h"
 #include "base/optional.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
@@ -276,7 +276,7 @@
     AssistantVisibility old_visibility,
     base::Optional<AssistantEntryPoint> entry_point,
     base::Optional<AssistantExitPoint> exit_point) {
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       new_visibility == AssistantVisibility::kVisible
           ? mojom::VoiceInteractionState::RUNNING
           : mojom::VoiceInteractionState::STOPPED);
@@ -352,8 +352,7 @@
 }
 
 void AssistantUiController::ShowUi(AssistantEntryPoint entry_point) {
-  auto* voice_interaction_controller =
-      Shell::Get()->voice_interaction_controller();
+  auto* voice_interaction_controller = VoiceInteractionController::Get();
 
   if (!voice_interaction_controller->settings_enabled().value_or(false) ||
       voice_interaction_controller->locked_full_screen_enabled().value_or(
@@ -362,7 +361,7 @@
   }
 
   // TODO(dmblack): Show a more helpful message to the user.
-  if (Shell::Get()->voice_interaction_controller()->voice_interaction_state() ==
+  if (VoiceInteractionController::Get()->voice_interaction_state() ==
       mojom::VoiceInteractionState::NOT_READY) {
     ShowToast(kUnboundServiceToastId, IDS_ASH_ASSISTANT_ERROR_GENERIC);
     return;
diff --git a/ash/assistant/assistant_view_delegate_impl.cc b/ash/assistant/assistant_view_delegate_impl.cc
index cf4cbec..a264a4a 100644
--- a/ash/assistant/assistant_view_delegate_impl.cc
+++ b/ash/assistant/assistant_view_delegate_impl.cc
@@ -9,8 +9,8 @@
 #include "ash/assistant/assistant_controller_observer.h"
 #include "ash/assistant/assistant_interaction_controller.h"
 #include "ash/assistant/assistant_notification_controller.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/shell.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 
 namespace ash {
@@ -93,12 +93,12 @@
 
 void AssistantViewDelegateImpl::AddVoiceInteractionControllerObserver(
     DefaultVoiceInteractionObserver* observer) {
-  Shell::Get()->voice_interaction_controller()->AddLocalObserver(observer);
+  VoiceInteractionController::Get()->AddLocalObserver(observer);
 }
 
 void AssistantViewDelegateImpl::RemoveVoiceInteractionControllerObserver(
     DefaultVoiceInteractionObserver* observer) {
-  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(observer);
+  VoiceInteractionController::Get()->RemoveLocalObserver(observer);
 }
 
 CaptionBarDelegate* AssistantViewDelegateImpl::GetCaptionBarDelegate() {
@@ -112,10 +112,8 @@
 }
 
 mojom::ConsentStatus AssistantViewDelegateImpl::GetConsentStatus() const {
-  return Shell::Get()
-      ->voice_interaction_controller()
-      ->consent_status()
-      .value_or(mojom::ConsentStatus::kUnknown);
+  return VoiceInteractionController::Get()->consent_status().value_or(
+      mojom::ConsentStatus::kUnknown);
 }
 
 ::wm::CursorManager* AssistantViewDelegateImpl::GetCursorManager() {
@@ -132,7 +130,7 @@
 }
 
 bool AssistantViewDelegateImpl::IsLaunchWithMicOpen() const {
-  return Shell::Get()->voice_interaction_controller()->launch_with_mic_open();
+  return VoiceInteractionController::Get()->launch_with_mic_open();
 }
 
 bool AssistantViewDelegateImpl::IsTabletMode() const {
diff --git a/ash/assistant/ui/DEPS b/ash/assistant/ui/DEPS
index d93a1786..bea464e 100644
--- a/ash/assistant/ui/DEPS
+++ b/ash/assistant/ui/DEPS
@@ -24,7 +24,6 @@
     "+ash/assistant/assistant_ui_controller.h",
     "+ash/shell.h",
     "+ash/test/ash_test_base.h",
-    "+ash/voice_interaction/voice_interaction_controller.h",
     "+ash/wm/tablet_mode/tablet_mode_controller.h",
     "+base/test/scoped_feature_list.h",
     "+chromeos/constants/chromeos_switches.h",
diff --git a/ash/assistant/ui/assistant_container_view_unittest.cc b/ash/assistant/ui/assistant_container_view_unittest.cc
index be61cb75..e096c463 100644
--- a/ash/assistant/ui/assistant_container_view_unittest.cc
+++ b/ash/assistant/ui/assistant_container_view_unittest.cc
@@ -8,9 +8,9 @@
 
 #include "ash/assistant/assistant_controller.h"
 #include "ash/assistant/assistant_ui_controller.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
@@ -38,7 +38,7 @@
     AshTestBase::SetUp();
 
     // Enable Assistant in settings.
-    Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true);
+    VoiceInteractionController::Get()->NotifySettingsEnabled(true);
 
     // Cache controller.
     controller_ = Shell::Get()->assistant_controller();
@@ -52,7 +52,7 @@
 
     // After mocks are set up our Assistant service is ready for use. Indicate
     // this by changing status from NOT_READY to STOPPED.
-    Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+    VoiceInteractionController::Get()->NotifyStatusChanged(
         mojom::VoiceInteractionState::STOPPED);
   }
 
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc
index b024d50..5b34142 100644
--- a/ash/autoclick/autoclick_controller.cc
+++ b/ash/autoclick/autoclick_controller.cc
@@ -420,8 +420,10 @@
       base::RecordAction(
           base::UserMetricsAction("Accessibility.Autoclick.DragAndDrop"));
       return;
+    case mojom::AutoclickEventType::kScroll:
+      // Scroll users actions will be recorded from AutoclickScrollView.
     case mojom::AutoclickEventType::kNoAction:
-      // No action shouldn't have a UserAction, so we return null.
+      // No action shouldn't have a UserAction, so we return.
       return;
   }
 }
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index cb6bf2d2..a87422a 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -24,6 +24,7 @@
 #include "ash/media/media_controller.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/shutdown_controller.h"
@@ -32,7 +33,6 @@
 #include "ash/system/network/vpn_list.h"
 #include "ash/system/night_light/night_light_controller.h"
 #include "ash/tray_action/tray_action.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
@@ -146,7 +146,7 @@
 
 void BindVoiceInteractionControllerRequestOnMainThread(
     mojom::VoiceInteractionControllerRequest request) {
-  Shell::Get()->voice_interaction_controller()->BindRequest(std::move(request));
+  VoiceInteractionController::Get()->BindRequest(std::move(request));
 }
 
 void BindVpnListRequestOnMainThread(mojom::VpnListRequest request) {
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index b55a905c..1888d96 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -157,6 +157,8 @@
     "tablet_mode_toggle_observer.h",
     "touch_uma.cc",
     "touch_uma.h",
+    "voice_interaction_controller.cc",
+    "voice_interaction_controller.h",
     "wallpaper_controller.cc",
     "wallpaper_controller.h",
     "wallpaper_controller_client.h",
@@ -256,6 +258,7 @@
     "power_utils_unittest.cc",
     "rounded_corner_decorator_unittest.cc",
     "shelf_model_unittest.cc",
+    "voice_interaction_controller_unittest.cc",
   ]
 
   deps = [
diff --git a/ash/public/cpp/voice_interaction_controller.cc b/ash/public/cpp/voice_interaction_controller.cc
new file mode 100644
index 0000000..2f12873
--- /dev/null
+++ b/ash/public/cpp/voice_interaction_controller.cc
@@ -0,0 +1,217 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/voice_interaction_controller.h"
+
+#include <utility>
+
+#include "chromeos/constants/chromeos_switches.h"
+
+namespace ash {
+namespace {
+VoiceInteractionController* g_voice_interaction_cotroller = nullptr;
+}
+
+// static
+VoiceInteractionController* VoiceInteractionController::Get() {
+  return g_voice_interaction_cotroller;
+}
+
+VoiceInteractionController::VoiceInteractionController() {
+  DCHECK(!g_voice_interaction_cotroller);
+  g_voice_interaction_cotroller = this;
+  if (chromeos::switches::IsAssistantEnabled())
+    voice_interaction_state_ = mojom::VoiceInteractionState::NOT_READY;
+}
+
+VoiceInteractionController::~VoiceInteractionController() {
+  DCHECK_EQ(g_voice_interaction_cotroller, this);
+  g_voice_interaction_cotroller = nullptr;
+}
+
+void VoiceInteractionController::BindRequest(
+    mojom::VoiceInteractionControllerRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void VoiceInteractionController::NotifyStatusChanged(
+    mojom::VoiceInteractionState state) {
+  if (voice_interaction_state_ == state)
+    return;
+
+  voice_interaction_state_ = state;
+  observers_.ForAllPtrs([state](auto* observer) {
+    observer->OnVoiceInteractionStatusChanged(state);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnVoiceInteractionStatusChanged(state);
+}
+
+void VoiceInteractionController::NotifySettingsEnabled(bool enabled) {
+  if (settings_enabled_.has_value() && settings_enabled_.value() == enabled)
+    return;
+
+  settings_enabled_ = enabled;
+  observers_.ForAllPtrs([enabled](auto* observer) {
+    observer->OnVoiceInteractionSettingsEnabled(enabled);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnVoiceInteractionSettingsEnabled(enabled);
+}
+
+void VoiceInteractionController::NotifyContextEnabled(bool enabled) {
+  if (context_enabled_.has_value() && context_enabled_.value() == enabled)
+    return;
+
+  context_enabled_ = enabled;
+  observers_.ForAllPtrs([enabled](auto* observer) {
+    observer->OnVoiceInteractionContextEnabled(enabled);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnVoiceInteractionContextEnabled(enabled);
+}
+
+void VoiceInteractionController::NotifyHotwordEnabled(bool enabled) {
+  if (hotword_enabled_.has_value() && hotword_enabled_.value() == enabled)
+    return;
+
+  hotword_enabled_ = enabled;
+  observers_.ForAllPtrs([enabled](auto* observer) {
+    observer->OnVoiceInteractionHotwordEnabled(enabled);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnVoiceInteractionHotwordEnabled(enabled);
+}
+
+void VoiceInteractionController::NotifyHotwordAlwaysOn(bool always_on) {
+  if (hotword_always_on_.has_value() && hotword_always_on_.value() == always_on)
+    return;
+
+  hotword_always_on_ = always_on;
+  observers_.ForAllPtrs([always_on](auto* observer) {
+    observer->OnVoiceInteractionHotwordAlwaysOn(always_on);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnVoiceInteractionHotwordAlwaysOn(always_on);
+}
+
+void VoiceInteractionController::NotifyConsentStatus(
+    mojom::ConsentStatus consent_status) {
+  if (consent_status_.has_value() && consent_status_.value() == consent_status)
+    return;
+
+  consent_status_ = consent_status;
+  observers_.ForAllPtrs([consent_status](auto* observer) {
+    observer->OnVoiceInteractionConsentStatusUpdated(consent_status);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnVoiceInteractionConsentStatusUpdated(consent_status);
+}
+
+void VoiceInteractionController::NotifyFeatureAllowed(
+    mojom::AssistantAllowedState state) {
+  if (allowed_state_ == state)
+    return;
+
+  allowed_state_ = state;
+  observers_.ForAllPtrs([state](auto* observer) {
+    observer->OnAssistantFeatureAllowedChanged(state);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnAssistantFeatureAllowedChanged(state);
+}
+
+void VoiceInteractionController::NotifyNotificationEnabled(bool enabled) {
+  notification_enabled_ = enabled;
+}
+
+void VoiceInteractionController::NotifyLocaleChanged(
+    const std::string& locale) {
+  if (locale_ == locale)
+    return;
+
+  locale_ = locale;
+  observers_.ForAllPtrs(
+      [locale](auto* observer) { observer->OnLocaleChanged(locale); });
+  for (auto& observer : local_observers_)
+    observer.OnLocaleChanged(locale);
+}
+
+void VoiceInteractionController::NotifyLaunchWithMicOpen(
+    bool launch_with_mic_open) {
+  launch_with_mic_open_ = launch_with_mic_open;
+}
+
+void VoiceInteractionController::NotifyArcPlayStoreEnabledChanged(
+    bool enabled) {
+  if (arc_play_store_enabled_ == enabled)
+    return;
+
+  arc_play_store_enabled_ = enabled;
+
+  observers_.ForAllPtrs([enabled](auto* observer) {
+    observer->OnArcPlayStoreEnabledChanged(enabled);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnArcPlayStoreEnabledChanged(enabled);
+}
+
+void VoiceInteractionController::NotifyLockedFullScreenStateChanged(
+    bool enabled) {
+  if (locked_full_screen_enabled_ == enabled)
+    return;
+
+  locked_full_screen_enabled_ = enabled;
+
+  observers_.ForAllPtrs([enabled](auto* observer) {
+    observer->OnLockedFullScreenStateChanged(enabled);
+  });
+  for (auto& observer : local_observers_)
+    observer.OnLockedFullScreenStateChanged(enabled);
+}
+
+void VoiceInteractionController::AddObserver(
+    mojom::VoiceInteractionObserverPtr observer) {
+  InitObserver(observer.get());
+  observers_.AddPtr(std::move(observer));
+}
+
+void VoiceInteractionController::AddLocalObserver(
+    DefaultVoiceInteractionObserver* observer) {
+  InitObserver(observer);
+  local_observers_.AddObserver(observer);
+}
+
+void VoiceInteractionController::RemoveLocalObserver(
+    DefaultVoiceInteractionObserver* observer) {
+  local_observers_.RemoveObserver(observer);
+}
+
+void VoiceInteractionController::InitObserver(
+    mojom::VoiceInteractionObserver* observer) {
+  if (voice_interaction_state_.has_value())
+    observer->OnVoiceInteractionStatusChanged(voice_interaction_state_.value());
+  if (settings_enabled_.has_value())
+    observer->OnVoiceInteractionSettingsEnabled(settings_enabled_.value());
+  if (context_enabled_.has_value())
+    observer->OnVoiceInteractionContextEnabled(context_enabled_.value());
+  if (hotword_enabled_.has_value())
+    observer->OnVoiceInteractionHotwordEnabled(hotword_enabled_.value());
+  if (consent_status_.has_value())
+    observer->OnVoiceInteractionConsentStatusUpdated(consent_status_.value());
+  if (hotword_always_on_.has_value())
+    observer->OnVoiceInteractionHotwordAlwaysOn(hotword_always_on_.value());
+  if (allowed_state_.has_value())
+    observer->OnAssistantFeatureAllowedChanged(allowed_state_.value());
+  if (locale_.has_value())
+    observer->OnLocaleChanged(locale_.value());
+  if (arc_play_store_enabled_.has_value())
+    observer->OnArcPlayStoreEnabledChanged(arc_play_store_enabled_.value());
+}
+
+void VoiceInteractionController::FlushForTesting() {
+  observers_.FlushForTesting();
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/voice_interaction_controller.h b/ash/public/cpp/voice_interaction_controller.h
new file mode 100644
index 0000000..bc87a0cf
--- /dev/null
+++ b/ash/public/cpp/voice_interaction_controller.h
@@ -0,0 +1,102 @@
+// Copyright 2019 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 ASH_PUBLIC_CPP_VOICE_INTERACTION_CONTROLLER_H_
+#define ASH_PUBLIC_CPP_VOICE_INTERACTION_CONTROLLER_H_
+
+#include <string>
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "ash/public/cpp/assistant/assistant_state_base.h"
+#include "ash/public/cpp/assistant/default_voice_interaction_observer.h"
+#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
+#include "base/observer_list.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+
+namespace ash {
+
+class ASH_PUBLIC_EXPORT VoiceInteractionController
+    : public mojom::VoiceInteractionController,
+      public AssistantStateBase {
+ public:
+  static VoiceInteractionController* Get();
+
+  VoiceInteractionController();
+  ~VoiceInteractionController() override;
+
+  void BindRequest(mojom::VoiceInteractionControllerRequest request);
+
+  // Called when the voice interaction state is changed.
+  virtual void NotifyStatusChanged(mojom::VoiceInteractionState state);
+
+  // Called when the voice interaction settings is enabled/disabled.
+  virtual void NotifySettingsEnabled(bool enabled);
+
+  // Called when the voice interaction context is enabled/disabled.
+  // If context is enabled the screenshot will be passed in voice
+  // interaction session.
+  virtual void NotifyContextEnabled(bool enabled);
+
+  // Called when the hotword listening is enabled/disabled.
+  virtual void NotifyHotwordEnabled(bool enabled);
+
+  // Called when the hotword is set to always on/only with power source.
+  virtual void NotifyHotwordAlwaysOn(bool always_on);
+
+  // Called when the consent status is obtained from the server.
+  virtual void NotifyConsentStatus(mojom::ConsentStatus consent_status);
+
+  // Notify if voice interaction feature is allowed or not. e.g. not allowed
+  // if disabled by policy.
+  virtual void NotifyFeatureAllowed(mojom::AssistantAllowedState state);
+
+  // Called when the notification is enabled/disabled.
+  virtual void NotifyNotificationEnabled(bool enabled);
+
+  // Called when the locale is changed.
+  virtual void NotifyLocaleChanged(const std::string& locale);
+
+  // Called when the launch with mic open state is changed.
+  virtual void NotifyLaunchWithMicOpen(bool launch_with_mic_open);
+
+  // Called when Google Play Store is enabled/disabled.
+  virtual void NotifyArcPlayStoreEnabledChanged(bool enabled);
+
+  // Called when locked full screen state is enabled/disabled.
+  virtual void NotifyLockedFullScreenStateChanged(bool enabled);
+
+  // ash::mojom::VoiceInteractionController:
+  void AddObserver(mojom::VoiceInteractionObserverPtr observer) override;
+
+  // Adding local observers in the same process.
+  void AddLocalObserver(DefaultVoiceInteractionObserver* observer);
+  void RemoveLocalObserver(DefaultVoiceInteractionObserver* observer);
+  void InitObserver(mojom::VoiceInteractionObserver* observer);
+
+  bool notification_enabled() const { return notification_enabled_; }
+
+  bool launch_with_mic_open() const { return launch_with_mic_open_; }
+
+  void FlushForTesting();
+
+ private:
+  // Whether notification is enabled.
+  bool notification_enabled_ = false;
+
+  // Whether the Assistant should launch with mic open;
+  bool launch_with_mic_open_ = false;
+
+  mojo::BindingSet<mojom::VoiceInteractionController> bindings_;
+
+  mojo::InterfacePtrSet<mojom::VoiceInteractionObserver> observers_;
+
+  base::ObserverList<DefaultVoiceInteractionObserver> local_observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(VoiceInteractionController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_VOICE_INTERACTION_CONTROLLER_H_
diff --git a/ash/public/cpp/voice_interaction_controller_unittest.cc b/ash/public/cpp/voice_interaction_controller_unittest.cc
new file mode 100644
index 0000000..261fcd4
--- /dev/null
+++ b/ash/public/cpp/voice_interaction_controller_unittest.cc
@@ -0,0 +1,174 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/voice_interaction_controller.h"
+
+#include <memory>
+#include <utility>
+
+#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+namespace {
+
+class TestVoiceInteractionObserver : public mojom::VoiceInteractionObserver {
+ public:
+  TestVoiceInteractionObserver() : voice_interaction_binding_(this) {}
+
+  ~TestVoiceInteractionObserver() override = default;
+
+  // mojom::VoiceInteractionObserver overrides:
+  void OnVoiceInteractionStatusChanged(
+      mojom::VoiceInteractionState state) override {
+    state_ = state;
+  }
+  void OnVoiceInteractionSettingsEnabled(bool enabled) override {
+    settings_enabled_ = enabled;
+  }
+  void OnVoiceInteractionContextEnabled(bool enabled) override {
+    context_enabled_ = enabled;
+  }
+  void OnVoiceInteractionHotwordAlwaysOn(bool always_on) override {
+    hotword_always_on_ = always_on;
+  }
+  void OnVoiceInteractionHotwordEnabled(bool enabled) override {
+    hotword_enabled_ = enabled;
+  }
+  void OnVoiceInteractionConsentStatusUpdated(
+      mojom::ConsentStatus consent_status) override {
+    consent_status_ = consent_status;
+  }
+  void OnAssistantFeatureAllowedChanged(
+      mojom::AssistantAllowedState state) override {}
+  void OnLocaleChanged(const std::string& locale) override {}
+  void OnArcPlayStoreEnabledChanged(bool enabled) override {
+    arc_play_store_enabled_ = enabled;
+  }
+  void OnLockedFullScreenStateChanged(bool enabled) override {}
+
+  mojom::VoiceInteractionState voice_interaction_state() const {
+    return state_;
+  }
+  bool settings_enabled() const { return settings_enabled_; }
+  bool context_enabled() const { return context_enabled_; }
+  bool hotword_always_on() const { return hotword_always_on_; }
+  bool hotword_enabled() const { return hotword_enabled_; }
+  bool arc_play_store_enabled() const { return arc_play_store_enabled_; }
+  mojom::ConsentStatus consent_status() const { return consent_status_; }
+
+  void SetVoiceInteractionController(VoiceInteractionController* controller) {
+    mojom::VoiceInteractionObserverPtr ptr;
+    voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr));
+    controller->AddObserver(std::move(ptr));
+  }
+
+ private:
+  mojom::VoiceInteractionState state_ = mojom::VoiceInteractionState::STOPPED;
+  bool settings_enabled_ = false;
+  bool context_enabled_ = false;
+  bool hotword_always_on_ = false;
+  bool hotword_enabled_ = false;
+  bool arc_play_store_enabled_ = false;
+  mojom::ConsentStatus consent_status_ = mojom::ConsentStatus::kUnknown;
+
+  mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestVoiceInteractionObserver);
+};
+
+class VoiceInteractionControllerTest : public testing::Test {
+ public:
+  VoiceInteractionControllerTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
+  ~VoiceInteractionControllerTest() override = default;
+
+  void SetUp() override {
+    controller_ = std::make_unique<VoiceInteractionController>();
+    observer_ = std::make_unique<TestVoiceInteractionObserver>();
+    observer_->SetVoiceInteractionController(controller());
+  }
+
+  void TearDown() override { observer_.reset(); }
+
+ protected:
+  VoiceInteractionController* controller() { return controller_.get(); }
+
+  TestVoiceInteractionObserver* observer() { return observer_.get(); }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<VoiceInteractionController> controller_;
+  std::unique_ptr<TestVoiceInteractionObserver> observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(VoiceInteractionControllerTest);
+};
+
+}  // namespace
+
+TEST_F(VoiceInteractionControllerTest, NotifyStatusChanged) {
+  controller()->NotifyStatusChanged(mojom::VoiceInteractionState::RUNNING);
+  controller()->FlushForTesting();
+
+  // The cached state should be updated.
+  EXPECT_EQ(mojom::VoiceInteractionState::RUNNING,
+            controller()->voice_interaction_state());
+  // The observers should be notified.
+  EXPECT_EQ(mojom::VoiceInteractionState::RUNNING,
+            observer()->voice_interaction_state());
+}
+
+TEST_F(VoiceInteractionControllerTest, NotifySettingsEnabled) {
+  controller()->NotifySettingsEnabled(true);
+  controller()->FlushForTesting();
+  // The cached state should be updated.
+  EXPECT_TRUE(controller()->settings_enabled());
+  // The observers should be notified.
+  EXPECT_TRUE(observer()->settings_enabled());
+}
+
+TEST_F(VoiceInteractionControllerTest, NotifyContextEnabled) {
+  controller()->NotifyContextEnabled(true);
+  controller()->FlushForTesting();
+  // The observers should be notified.
+  EXPECT_TRUE(observer()->context_enabled());
+}
+
+TEST_F(VoiceInteractionControllerTest, NotifyHotwordAlwaysOn) {
+  controller()->NotifyHotwordAlwaysOn(true);
+  controller()->FlushForTesting();
+  // The observers should be notified.
+  EXPECT_TRUE(observer()->hotword_always_on());
+}
+
+TEST_F(VoiceInteractionControllerTest, NotifyHotwordEnabled) {
+  controller()->NotifyHotwordEnabled(true);
+  controller()->FlushForTesting();
+  // The observers should be notified.
+  EXPECT_TRUE(observer()->hotword_enabled());
+}
+
+TEST_F(VoiceInteractionControllerTest, NotifyConsentStatus) {
+  controller()->NotifyConsentStatus(
+      mojom::ConsentStatus::kActivityControlAccepted);
+  controller()->FlushForTesting();
+  // The cached state should be updated.
+  EXPECT_TRUE(controller()->consent_status() ==
+              mojom::ConsentStatus::kActivityControlAccepted);
+  // The observers should be notified.
+  EXPECT_TRUE(observer()->consent_status() ==
+              mojom::ConsentStatus::kActivityControlAccepted);
+}
+
+TEST_F(VoiceInteractionControllerTest, NotifyArcPlayStoreEnabledChanged) {
+  controller()->NotifyArcPlayStoreEnabledChanged(true);
+  controller()->FlushForTesting();
+  // The observers should be notified.
+  EXPECT_TRUE(observer()->arc_play_store_enabled());
+}
+
+}  // namespace ash
diff --git a/ash/public/interfaces/accessibility_controller_enums.mojom b/ash/public/interfaces/accessibility_controller_enums.mojom
index 9c23483..8254baa1 100644
--- a/ash/public/interfaces/accessibility_controller_enums.mojom
+++ b/ash/public/interfaces/accessibility_controller_enums.mojom
@@ -84,22 +84,24 @@
 // should be added at the end.
 enum AutoclickEventType {
   // Perform a left click.
-  kLeftClick,
+  kLeftClick = 0,
 
   // Perform a right click.
-  kRightClick,
+  kRightClick = 1,
 
   // Perform a drag and drop, i.e. click down at the first dwell, and up at the
   // second dwell.
-  kDragAndDrop,
+  kDragAndDrop = 2,
 
   // Perform a double-click.
-  kDoubleClick,
+  kDoubleClick = 3,
 
   // A non-action, i.e. nothing will happen at the end of the dwell time.
-  kNoAction,
+  kNoAction = 4,
 
-  // TODO(katie): Add scroll.
+  // A mousewheel scroll action. An additional menu will be shown for the user
+  // to pick whether they want to scroll up/down/left/right.
+  kScroll = 5,
 };
 
 // The Automatic Clicks feature's on-screen menu display location. These values
diff --git a/ash/public/interfaces/voice_interaction_controller.mojom b/ash/public/interfaces/voice_interaction_controller.mojom
index 7b3136da..efc163d 100644
--- a/ash/public/interfaces/voice_interaction_controller.mojom
+++ b/ash/public/interfaces/voice_interaction_controller.mojom
@@ -88,45 +88,6 @@
 // Interface for ash client (Chrome) to connect to the voice interaction
 // controller, which notifies changes of voice interaction related flags.
 interface VoiceInteractionController {
-  // Called when the voice interaction state is changed.
-  NotifyStatusChanged(VoiceInteractionState state);
-
-  // Called when the voice interaction settings is enabled/disabled.
-  NotifySettingsEnabled(bool enabled);
-
-  // Called when the voice interaction context is enabled/disabled.
-  // If context is enabled the screenshot will be passed in voice
-  // interaction session.
-  NotifyContextEnabled(bool enabled);
-
-  // Called when the hotword listening is enabled/disabled.
-  NotifyHotwordEnabled(bool enabled);
-
-  // Called when the hotword is set to always on/only with power source.
-  NotifyHotwordAlwaysOn(bool enabled);
-
-  // Called when the consent status is obtained from the server.
-  NotifyConsentStatus(ConsentStatus consent_status);
-
-  // Notify if voice interaction feature is allowed or not. e.g. not allowed
-  // if disabled by policy.
-  NotifyFeatureAllowed(AssistantAllowedState state);
-
-  // Called when the notification is enabled/disabled.
-  NotifyNotificationEnabled(bool enabled);
-
-  // Called when the locale is changed.
-  NotifyLocaleChanged(string locale);
-
-  // Called when the launch with mic open state is changed.
-  NotifyLaunchWithMicOpen(bool launch_with_mic_open);
-
-  // Called when Google Play Store is enabled/disabled.
-  NotifyArcPlayStoreEnabledChanged(bool enabled);
-
-  // Called when locked full screen state is enabled/disabled.
-  NotifyLockedFullScreenStateChanged(bool enabled);
-
   // Add an observer.
   AddObserver(VoiceInteractionObserver observer);
 };
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index 8f116a99..258be518 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -21,6 +21,7 @@
     "autoclick_position_bottom_right.icon",
     "autoclick_position_top_left.icon",
     "autoclick_position_top_right.icon",
+    "autoclick_scroll.icon",
     "captive_portal.icon",
     "check_circle.icon",
     "desks_close_desk_button.icon",
diff --git a/ash/resources/vector_icons/autoclick_scroll.icon b/ash/resources/vector_icons/autoclick_scroll.icon
new file mode 100644
index 0000000..0290114
--- /dev/null
+++ b/ash/resources/vector_icons/autoclick_scroll.icon
@@ -0,0 +1,38 @@
+// Copyright 2019 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.
+
+CANVAS_DIMENSIONS, 20,
+R_MOVE_TO, 9, 11,
+R_H_LINE_TO, -4.71f,
+R_LINE_TO, 1.95f, 1.87f,
+R_LINE_TO, -1.15f, 1.13f,
+R_LINE_TO, -4.09f, -4,
+R_LINE_TO, 4.09f, -4,
+R_LINE_TO, 1.15f, 1.13f,
+R_LINE_TO, -1.87f, 1.87f,
+R_H_LINE_TO, 4.63f,
+R_V_LINE_TO, -4.71f,
+R_LINE_TO, -1.87f, 1.95f,
+R_LINE_TO, -1.13f, -1.15f,
+R_LINE_TO, 4, -4.09f,
+R_LINE_TO, 4, 4.09f,
+R_LINE_TO, -1.13f, 1.15f,
+R_LINE_TO, -1.87f, -1.87f,
+R_V_LINE_TO, 4.63f,
+R_H_LINE_TO, 4.71f,
+R_LINE_TO, -1.95f, -1.87f,
+R_LINE_TO, 1.15f, -1.13f,
+R_LINE_TO, 4.09f, 4,
+R_LINE_TO, -4.09f, 4,
+R_LINE_TO, -1.15f, -1.13f,
+R_LINE_TO, 1.87f, -1.87f,
+R_H_LINE_TO, -4.63f,
+R_V_LINE_TO, 4.71f,
+R_LINE_TO, 1.87f, -1.95f,
+R_LINE_TO, 1.13f, 1.15f,
+R_LINE_TO, -4, 4.09f,
+R_LINE_TO, -4, -4.09f,
+R_LINE_TO, 1.13f, -1.15f,
+R_LINE_TO, 1.87f, 1.87f,
+CLOSE
diff --git a/ash/shelf/app_list_button_controller.cc b/ash/shelf/app_list_button_controller.cc
index 705ea72..bd9ba84 100644
--- a/ash/shelf/app_list_button_controller.cc
+++ b/ash/shelf/app_list_button_controller.cc
@@ -7,6 +7,7 @@
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/assistant/assistant_controller.h"
 #include "ash/home_screen/home_screen_controller.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/assistant_overlay.h"
@@ -14,7 +15,6 @@
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
 #include "ash/shell_state.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/bind.h"
 #include "base/logging.h"
@@ -53,7 +53,7 @@
     shell->app_list_controller()->AddObserver(this);
   shell->session_controller()->AddObserver(this);
   shell->tablet_mode_controller()->AddObserver(this);
-  shell->voice_interaction_controller()->AddLocalObserver(this);
+  VoiceInteractionController::Get()->AddLocalObserver(this);
 
   // Initialize voice interaction overlay and sync the flags if active user
   // session has already started. This could happen when an external monitor
@@ -74,7 +74,7 @@
   if (shell->tablet_mode_controller())
     shell->tablet_mode_controller()->RemoveObserver(this);
   shell->session_controller()->RemoveObserver(this);
-  shell->voice_interaction_controller()->RemoveLocalObserver(this);
+  VoiceInteractionController::Get()->RemoveLocalObserver(this);
 }
 
 bool AppListButtonController::MaybeHandleGestureEvent(ui::GestureEvent* event,
@@ -146,8 +146,7 @@
 }
 
 bool AppListButtonController::IsVoiceInteractionAvailable() {
-  VoiceInteractionController* controller =
-      Shell::Get()->voice_interaction_controller();
+  VoiceInteractionController* controller = VoiceInteractionController::Get();
   bool settings_enabled = controller->settings_enabled().value_or(false);
   bool consent_given = controller->consent_status() ==
                        mojom::ConsentStatus::kActivityControlAccepted;
@@ -159,10 +158,8 @@
 }
 
 bool AppListButtonController::IsVoiceInteractionRunning() {
-  return Shell::Get()
-             ->voice_interaction_controller()
-             ->voice_interaction_state()
-             .value_or(mojom::VoiceInteractionState::STOPPED) ==
+  return VoiceInteractionController::Get()->voice_interaction_state().value_or(
+             mojom::VoiceInteractionState::STOPPED) ==
          mojom::VoiceInteractionState::RUNNING;
 }
 
diff --git a/ash/shelf/app_list_button_unittest.cc b/ash/shelf/app_list_button_unittest.cc
index c5cb16be..1ecad65 100644
--- a/ash/shelf/app_list_button_unittest.cc
+++ b/ash/shelf/app_list_button_unittest.cc
@@ -16,6 +16,7 @@
 #include "ash/kiosk_next/kiosk_next_shell_test_util.h"
 #include "ash/kiosk_next/mock_kiosk_next_shell_client.h"
 #include "ash/public/cpp/ash_features.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
@@ -24,7 +25,6 @@
 #include "ash/shelf/shelf_view_test_api.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
@@ -192,10 +192,10 @@
   CreateUserSessions(2);
 
   // Enable voice interaction in system settings.
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true);
-  Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed(
+  VoiceInteractionController::Get()->NotifySettingsEnabled(true);
+  VoiceInteractionController::Get()->NotifyFeatureAllowed(
       mojom::AssistantAllowedState::ALLOWED);
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       mojom::VoiceInteractionState::STOPPED);
 
   ui::GestureEvent long_press =
@@ -222,11 +222,11 @@
 
 TEST_F(VoiceInteractionAppListButtonTest, LongPressGestureWithSecondaryUser) {
   // Disallowed by secondary user.
-  Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed(
+  VoiceInteractionController::Get()->NotifyFeatureAllowed(
       mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER);
 
   // Enable voice interaction in system settings.
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true);
+  VoiceInteractionController::Get()->NotifySettingsEnabled(true);
 
   ui::GestureEvent long_press =
       CreateGestureEvent(ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
@@ -254,8 +254,8 @@
 
   // Simulate a user who has already completed setup flow, but disabled voice
   // interaction in settings.
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(false);
-  Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed(
+  VoiceInteractionController::Get()->NotifySettingsEnabled(false);
+  VoiceInteractionController::Get()->NotifyFeatureAllowed(
       mojom::AssistantAllowedState::ALLOWED);
 
   ui::GestureEvent long_press =
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index b194444..4612824 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -368,8 +368,6 @@
       shelf_layout_manager_->GetShelfBackgroundType(),
       AnimationChangeType::IMMEDIATE);
 
-  views::Widget::AddObserver(this);
-
   background_animator_.AddObserver(delegate_view_);
   shelf_->AddObserver(this);
 
@@ -414,7 +412,6 @@
   // Don't need to observe focus/activation during shutdown.
   Shell::Get()->focus_cycler()->RemoveWidget(this);
   SetFocusCycler(nullptr);
-  RemoveObserver(this);
 }
 
 void ShelfWidget::CreateStatusAreaWidget(aura::Window* status_container) {
@@ -522,20 +519,22 @@
   to_focus->GetFocusManager()->SetFocusedView(to_focus);
 }
 
-void ShelfWidget::OnWidgetActivationChanged(views::Widget* widget,
-                                            bool active) {
+bool ShelfWidget::OnNativeWidgetActivationChanged(bool active) {
+  if (!Widget::OnNativeWidgetActivationChanged(active))
+    return false;
   if (active) {
     // Do not focus the default element if the widget activation came from the
     // another widget's focus cycling. The setter of
     // |activated_from_other_widget_| should handle focusing the correct view.
     if (activated_from_other_widget_) {
       activated_from_other_widget_ = false;
-      return;
+      return true;
     }
     delegate_view_->SetPaneFocusAndFocusDefault();
   } else {
     delegate_view_->GetFocusManager()->ClearFocus();
   }
+  return true;
 }
 
 void ShelfWidget::WillDeleteShelfLayoutManager() {
diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h
index 4f804e1..35c993c12 100644
--- a/ash/shelf/shelf_widget.h
+++ b/ash/shelf/shelf_widget.h
@@ -16,7 +16,6 @@
 #include "ash/shelf/shelf_observer.h"
 #include "base/macros.h"
 #include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_observer.h"
 
 namespace app_list {
 class ApplicationDragAndDropHost;
@@ -37,7 +36,6 @@
 // the status area widget. There is one ShelfWidget per display. It is created
 // early during RootWindowController initialization.
 class ASH_EXPORT ShelfWidget : public views::Widget,
-                               public views::WidgetObserver,
                                public ShelfLayoutManagerObserver,
                                public ShelfObserver,
                                public SessionObserver,
@@ -106,10 +104,12 @@
   // and focuses it.
   void FocusFirstOrLastFocusableChild(bool last);
 
-  // Overridden from views::WidgetObserver:
-  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
+  // views::Widget:
+  void OnMouseEvent(ui::MouseEvent* event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
+  bool OnNativeWidgetActivationChanged(bool active) override;
 
-  // ShelfLayoutManagerObserver overrides:
+  // ShelfLayoutManagerObserver:
   void WillDeleteShelfLayoutManager() override;
 
   // ShelfObserver:
@@ -148,10 +148,6 @@
   // Shows shelf widget if IsVisible() returns false.
   void ShowIfHidden();
 
-  // views::Widget:
-  void OnMouseEvent(ui::MouseEvent* event) override;
-  void OnGestureEvent(ui::GestureEvent* event) override;
-
   Shelf* shelf_;
 
   ShelfBackgroundAnimator background_animator_;
diff --git a/ash/shell.cc b/ash/shell.cc
index eccb4cf..843e429 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -72,6 +72,7 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/root_window_controller.h"
 #include "ash/screenshot_delegate.h"
 #include "ash/session/session_controller_impl.h"
@@ -119,7 +120,6 @@
 #include "ash/touch/touch_devices_controller.h"
 #include "ash/tray_action/tray_action.h"
 #include "ash/utility/screenshot_controller.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "ash/wayland/wayland_server_controller.h"
 #include "ash/wm/ash_focus_rules.h"
diff --git a/ash/shell.h b/ash/shell.h
index be213d4b..d8c2019 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -492,9 +492,6 @@
   }
   UserMetricsRecorder* metrics() { return user_metrics_recorder_.get(); }
   VideoDetector* video_detector() { return video_detector_.get(); }
-  VoiceInteractionController* voice_interaction_controller() {
-    return voice_interaction_controller_.get();
-  }
   VpnList* vpn_list() { return vpn_list_.get(); }
   WallpaperControllerImpl* wallpaper_controller() {
     return wallpaper_controller_.get();
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.cc b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
index 7759591c..c6cb458bc 100644
--- a/ash/system/accessibility/autoclick_menu_bubble_controller.cc
+++ b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
@@ -15,6 +15,8 @@
 #include "ash/wm/work_area_insets.h"
 #include "ash/wm/workspace/workspace_layout_manager.h"
 #include "ash/wm/workspace_controller.h"
+#include "base/command_line.h"
+#include "ui/accessibility/accessibility_switches.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
@@ -25,6 +27,7 @@
 namespace {
 // Autoclick menu constants.
 const int kAutoclickMenuWidth = 321;
+const int kAutoclickMenuWidthWithScroll = 371;
 const int kAutoclickMenuHeight = 64;
 }  // namespace
 
@@ -72,27 +75,30 @@
   aura::Window* window = Shell::GetPrimaryRootWindow();
   gfx::Rect work_area =
       WorkAreaInsets::ForWindow(window)->user_work_area_bounds();
+  int width = base::CommandLine::ForCurrentProcess()->HasSwitch(
+                  switches::kEnableExperimentalAccessibilityAutoclick)
+                  ? kAutoclickMenuWidthWithScroll
+                  : kAutoclickMenuWidth;
   gfx::Rect new_bounds;
   switch (new_position) {
     case mojom::AutoclickMenuPosition::kBottomRight:
-      new_bounds = gfx::Rect(work_area.right() - kAutoclickMenuWidth,
-                             work_area.bottom() - kAutoclickMenuHeight,
-                             kAutoclickMenuWidth, kAutoclickMenuHeight);
+      new_bounds = gfx::Rect(work_area.right() - width,
+                             work_area.bottom() - kAutoclickMenuHeight, width,
+                             kAutoclickMenuHeight);
       break;
     case mojom::AutoclickMenuPosition::kBottomLeft:
       new_bounds =
           gfx::Rect(work_area.x(), work_area.bottom() - kAutoclickMenuHeight,
-                    kAutoclickMenuWidth, kAutoclickMenuHeight);
+                    width, kAutoclickMenuHeight);
       break;
     case mojom::AutoclickMenuPosition::kTopLeft:
       // Setting the top to 1 instead of 0 so that the view is drawn on screen.
-      new_bounds = gfx::Rect(work_area.x(), 1, kAutoclickMenuWidth,
-                             kAutoclickMenuHeight);
+      new_bounds = gfx::Rect(work_area.x(), 1, width, kAutoclickMenuHeight);
       break;
     case mojom::AutoclickMenuPosition::kTopRight:
       // Setting the top to 1 instead of 0 so that the view is drawn on screen.
-      new_bounds = gfx::Rect(work_area.right() - kAutoclickMenuWidth, 1,
-                             kAutoclickMenuWidth, kAutoclickMenuHeight);
+      new_bounds =
+          gfx::Rect(work_area.right() - width, 1, width, kAutoclickMenuHeight);
       break;
     case mojom::AutoclickMenuPosition::kSystemDefault:
       return;
@@ -135,8 +141,12 @@
       Shell::GetPrimaryRootWindow(), kShellWindowId_AutoclickContainer);
   init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
   init_params.insets = gfx::Insets(kCollisionWindowWorkAreaInsetsDp);
-  init_params.min_width = kAutoclickMenuWidth;
-  init_params.max_width = kAutoclickMenuWidth;
+  int width = base::CommandLine::ForCurrentProcess()->HasSwitch(
+                  switches::kEnableExperimentalAccessibilityAutoclick)
+                  ? kAutoclickMenuWidthWithScroll
+                  : kAutoclickMenuWidth;
+  init_params.min_width = width;
+  init_params.max_width = width;
   init_params.corner_radius = kUnifiedTrayCornerRadius;
   init_params.has_shadow = false;
   bubble_view_ = new AutoclickMenuBubbleView(init_params);
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.h b/ash/system/accessibility/autoclick_menu_bubble_controller.h
index 0a21190..9c99179 100644
--- a/ash/system/accessibility/autoclick_menu_bubble_controller.h
+++ b/ash/system/accessibility/autoclick_menu_bubble_controller.h
@@ -33,11 +33,11 @@
   // Shows or hides the bubble.
   void SetBubbleVisibility(bool is_visible);
 
-  // Performs the mouse events on the bubble. at the given location in DIPs.
+  // Performs a mouse event on the bubble at the given location in DIPs.
   void ClickOnBubble(gfx::Point location_in_dips, int mouse_event_flags);
 
-  // Whether the tray button or the bubble, if the bubble exists, contain
-  // the given screen point.
+  // Whether the the bubble, if the bubble exists, contains the given screen
+  // point.
   bool ContainsPointInScreen(const gfx::Point& point);
 
   // TrayBubbleView::Delegate:
diff --git a/ash/system/accessibility/autoclick_menu_view.cc b/ash/system/accessibility/autoclick_menu_view.cc
index 7ba111e2..fad944d 100644
--- a/ash/system/accessibility/autoclick_menu_view.cc
+++ b/ash/system/accessibility/autoclick_menu_view.cc
@@ -10,8 +10,10 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/top_shortcut_button.h"
+#include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "ui/accessibility/accessibility_switches.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -175,6 +177,13 @@
                                   kAutoclickPositionBottomLeftIcon,
                                   IDS_ASH_AUTOCLICK_OPTION_CHANGE_POSITION,
                                   kPanelPositionButtonSize)) {
+  // TODO(katie): Initialize scroll above once it launches, target in M77.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableExperimentalAccessibilityAutoclick)) {
+    scroll_button_ = new AutoclickMenuButton(this, kAutoclickScrollIcon,
+                                             IDS_ASH_AUTOCLICK_OPTION_SCROLL);
+  }
+
   // Set view IDs for testing.
   left_click_button_->SetId(ButtonId::kLeftClick);
   right_click_button_->SetId(ButtonId::kRightClick);
@@ -182,6 +191,8 @@
   drag_button_->SetId(ButtonId::kDragAndDrop);
   pause_button_->SetId(ButtonId::kPause);
   position_button_->SetId(ButtonId::kPosition);
+  if (scroll_button_)
+    scroll_button_->SetId(ButtonId::kScroll);
 
   std::unique_ptr<views::BoxLayout> layout = std::make_unique<views::BoxLayout>(
       views::BoxLayout::kHorizontal, gfx::Insets(), 0);
@@ -197,6 +208,8 @@
   action_button_container->AddChildView(right_click_button_);
   action_button_container->AddChildView(double_click_button_);
   action_button_container->AddChildView(drag_button_);
+  if (scroll_button_)
+    action_button_container->AddChildView(scroll_button_);
   action_button_container->AddChildView(pause_button_);
   AddChildView(action_button_container);
 
@@ -230,6 +243,8 @@
   double_click_button_->SetToggled(type ==
                                    mojom::AutoclickEventType::kDoubleClick);
   drag_button_->SetToggled(type == mojom::AutoclickEventType::kDragAndDrop);
+  if (scroll_button_)
+    scroll_button_->SetToggled(type == mojom::AutoclickEventType::kScroll);
   pause_button_->SetToggled(type == mojom::AutoclickEventType::kNoAction);
   if (type != mojom::AutoclickEventType::kNoAction)
     event_type_ = type;
@@ -297,6 +312,8 @@
     type = mojom::AutoclickEventType::kDoubleClick;
   } else if (sender == drag_button_) {
     type = mojom::AutoclickEventType::kDragAndDrop;
+  } else if (sender == scroll_button_) {
+    type = mojom::AutoclickEventType::kScroll;
   } else if (sender == pause_button_) {
     // If the pause button was already selected, tapping it again turns off
     // pause and returns to the previous type.
diff --git a/ash/system/accessibility/autoclick_menu_view.h b/ash/system/accessibility/autoclick_menu_view.h
index 51384ce..3cd5ac9 100644
--- a/ash/system/accessibility/autoclick_menu_view.h
+++ b/ash/system/accessibility/autoclick_menu_view.h
@@ -41,7 +41,8 @@
     kRightClick = 3,
     kDoubleClick = 4,
     kDragAndDrop = 5,
-    kPause = 6
+    kScroll = 6,
+    kPause = 7,
   };
 
   AutoclickMenuView(mojom::AutoclickEventType type,
@@ -63,6 +64,7 @@
   AutoclickMenuButton* right_click_button_;
   AutoclickMenuButton* double_click_button_;
   AutoclickMenuButton* drag_button_;
+  AutoclickMenuButton* scroll_button_ = nullptr;
   AutoclickMenuButton* pause_button_;
   AutoclickMenuButton* position_button_;
 
diff --git a/ash/system/palette/palette_tray_unittest.cc b/ash/system/palette/palette_tray_unittest.cc
index 7949626d..a98387f 100644
--- a/ash/system/palette/palette_tray_unittest.cc
+++ b/ash/system/palette/palette_tray_unittest.cc
@@ -17,6 +17,7 @@
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/stylus_utils.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
@@ -30,7 +31,6 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test_shell_delegate.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
@@ -345,11 +345,11 @@
 TEST_F(PaletteTrayTestWithVoiceInteraction, MetalayerToolActivatesHighlighter) {
   ui::ScopedAnimationDurationScaleMode animation_duration_mode(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       mojom::VoiceInteractionState::RUNNING);
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true);
-  Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(true);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifySettingsEnabled(true);
+  VoiceInteractionController::Get()->NotifyContextEnabled(true);
+  VoiceInteractionController::Get()->FlushForTesting();
 
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->EnterPenPointerMode();
@@ -411,8 +411,8 @@
   // Disabling metalayer support in the delegate should disable the palette
   // tool.
   test_api_->palette_tool_manager()->ActivateTool(PaletteToolId::METALAYER);
-  Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(false);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifyContextEnabled(false);
+  VoiceInteractionController::Get()->FlushForTesting();
   EXPECT_FALSE(metalayer_enabled());
 
   // With the metalayer disabled again, press/drag does not activate the
@@ -426,11 +426,11 @@
        StylusBarrelButtonActivatesHighlighter) {
   ui::ScopedAnimationDurationScaleMode animation_duration_mode(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       mojom::VoiceInteractionState::NOT_READY);
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(false);
-  Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(false);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifySettingsEnabled(false);
+  VoiceInteractionController::Get()->NotifyContextEnabled(false);
+  VoiceInteractionController::Get()->FlushForTesting();
 
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->EnterPenPointerMode();
@@ -450,23 +450,23 @@
                              false /* no highlighter on press */);
 
   // Enable one of the two user prefs, should not be sufficient.
-  Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(true);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifyContextEnabled(true);
+  VoiceInteractionController::Get()->FlushForTesting();
   WaitDragAndAssertMetalayer("one pref enabled", origin,
                              ui::EF_LEFT_MOUSE_BUTTON, false /* no metalayer */,
                              false /* no highlighter on press */);
 
   // Enable the other user pref, still not sufficient.
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifySettingsEnabled(true);
+  VoiceInteractionController::Get()->FlushForTesting();
   WaitDragAndAssertMetalayer("two prefs enabled", origin,
                              ui::EF_LEFT_MOUSE_BUTTON, false /* no metalayer */,
                              false /* no highlighter on press */);
 
   // Once the service is ready, the button should start working.
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       mojom::VoiceInteractionState::RUNNING);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->FlushForTesting();
 
   // Press and drag with no button, still no highlighter.
   WaitDragAndAssertMetalayer("all enabled, no button ", origin, ui::EF_NONE,
@@ -530,8 +530,8 @@
 
   // Disable the metalayer support.
   // This should deactivate both the palette tool and the highlighter.
-  Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(false);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifyContextEnabled(false);
+  VoiceInteractionController::Get()->FlushForTesting();
   EXPECT_FALSE(test_api_->palette_tool_manager()->IsToolActive(
       PaletteToolId::METALAYER));
 
diff --git a/ash/system/palette/tools/metalayer_mode.cc b/ash/system/palette/tools/metalayer_mode.cc
index 64979b0..c76e7d6 100644
--- a/ash/system/palette/tools/metalayer_mode.cc
+++ b/ash/system/palette/tools/metalayer_mode.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/palette/tools/metalayer_mode.h"
 
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -13,7 +14,6 @@
 #include "ash/system/toast/toast_manager.h"
 #include "ash/system/tray/hover_highlight_view.h"
 #include "ash/system/tray/tray_constants.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/bind.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/event.h"
@@ -39,13 +39,13 @@
     : CommonPaletteTool(delegate),
       weak_factory_(this) {
   Shell::Get()->AddPreTargetHandler(this);
-  Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
+  VoiceInteractionController::Get()->AddLocalObserver(this);
   Shell::Get()->highlighter_controller()->AddObserver(this);
 }
 
 MetalayerMode::~MetalayerMode() {
   Shell::Get()->highlighter_controller()->RemoveObserver(this);
-  Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
+  VoiceInteractionController::Get()->RemoveLocalObserver(this);
   Shell::Get()->RemovePreTargetHandler(this);
 }
 
diff --git a/ash/system/palette/tools/metalayer_unittest.cc b/ash/system/palette/tools/metalayer_unittest.cc
index abc72d0..f55e501 100644
--- a/ash/system/palette/tools/metalayer_unittest.cc
+++ b/ash/system/palette/tools/metalayer_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/highlighter/highlighter_controller.h"
 #include "ash/highlighter/highlighter_controller_test_api.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "ash/shell.h"
 #include "ash/system/palette/mock_palette_tool_delegate.h"
@@ -14,7 +15,6 @@
 #include "ash/system/palette/tools/metalayer_mode.h"
 #include "ash/system/tray/hover_highlight_view.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/voice_interaction/voice_interaction_controller.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -88,15 +88,12 @@
           const bool ready = state != mojom::VoiceInteractionState::NOT_READY;
           const bool selectable = allowed && enabled && context && ready;
 
-          Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
-              state);
-          Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(
-              enabled);
-          Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(
-              context);
-          Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed(
+          VoiceInteractionController::Get()->NotifyStatusChanged(state);
+          VoiceInteractionController::Get()->NotifySettingsEnabled(enabled);
+          VoiceInteractionController::Get()->NotifyContextEnabled(context);
+          VoiceInteractionController::Get()->NotifyFeatureAllowed(
               allowed_state);
-          Shell::Get()->voice_interaction_controller()->FlushForTesting();
+          VoiceInteractionController::Get()->FlushForTesting();
 
           std::unique_ptr<views::View> view =
               base::WrapUnique(tool_->CreateView());
@@ -142,30 +139,30 @@
 
 // Verifies that disabling the metalayer support disables the tool.
 TEST_F(MetalayerToolTest, MetalayerUnsupportedDisablesPaletteTool) {
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       mojom::VoiceInteractionState::RUNNING);
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true);
-  Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(true);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifySettingsEnabled(true);
+  VoiceInteractionController::Get()->NotifyContextEnabled(true);
+  VoiceInteractionController::Get()->FlushForTesting();
 
   // Disabling the user prefs individually should disable the tool.
   tool_->OnEnable();
   EXPECT_CALL(*palette_tool_delegate_.get(),
               DisableTool(PaletteToolId::METALAYER));
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(false);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifySettingsEnabled(false);
+  VoiceInteractionController::Get()->FlushForTesting();
   testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
-  Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifySettingsEnabled(true);
+  VoiceInteractionController::Get()->FlushForTesting();
 
   tool_->OnEnable();
   EXPECT_CALL(*palette_tool_delegate_.get(),
               DisableTool(PaletteToolId::METALAYER));
-  Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(false);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifyContextEnabled(false);
+  VoiceInteractionController::Get()->FlushForTesting();
   testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
-  Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(true);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->NotifyContextEnabled(true);
+  VoiceInteractionController::Get()->FlushForTesting();
 
   // Test VoiceInteractionState changes.
   tool_->OnEnable();
@@ -175,19 +172,19 @@
   EXPECT_CALL(*palette_tool_delegate_.get(),
               DisableTool(PaletteToolId::METALAYER))
       .Times(0);
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       mojom::VoiceInteractionState::STOPPED);
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       mojom::VoiceInteractionState::RUNNING);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->FlushForTesting();
   testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
 
   // Changing the state to NOT_READY should disable the tool.
   EXPECT_CALL(*palette_tool_delegate_.get(),
               DisableTool(PaletteToolId::METALAYER));
-  Shell::Get()->voice_interaction_controller()->NotifyStatusChanged(
+  VoiceInteractionController::Get()->NotifyStatusChanged(
       mojom::VoiceInteractionState::NOT_READY);
-  Shell::Get()->voice_interaction_controller()->FlushForTesting();
+  VoiceInteractionController::Get()->FlushForTesting();
   testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
 }
 
diff --git a/ash/voice_interaction/OWNERS b/ash/voice_interaction/OWNERS
deleted file mode 100644
index bb65116..0000000
--- a/ash/voice_interaction/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *_struct_traits*.*=set noparent
-per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/ash/voice_interaction/voice_interaction_controller.cc b/ash/voice_interaction/voice_interaction_controller.cc
deleted file mode 100644
index 01bca6f..0000000
--- a/ash/voice_interaction/voice_interaction_controller.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/voice_interaction/voice_interaction_controller.h"
-
-#include <utility>
-
-#include "chromeos/constants/chromeos_switches.h"
-
-namespace ash {
-
-VoiceInteractionController::VoiceInteractionController() {
-  if (chromeos::switches::IsAssistantEnabled())
-    voice_interaction_state_ = mojom::VoiceInteractionState::NOT_READY;
-}
-
-VoiceInteractionController::~VoiceInteractionController() = default;
-
-void VoiceInteractionController::BindRequest(
-    mojom::VoiceInteractionControllerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
-void VoiceInteractionController::NotifyStatusChanged(
-    mojom::VoiceInteractionState state) {
-  if (voice_interaction_state_ == state)
-    return;
-
-  voice_interaction_state_ = state;
-  observers_.ForAllPtrs([state](auto* observer) {
-    observer->OnVoiceInteractionStatusChanged(state);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnVoiceInteractionStatusChanged(state);
-}
-
-void VoiceInteractionController::NotifySettingsEnabled(bool enabled) {
-  if (settings_enabled_.has_value() && settings_enabled_.value() == enabled)
-    return;
-
-  settings_enabled_ = enabled;
-  observers_.ForAllPtrs([enabled](auto* observer) {
-    observer->OnVoiceInteractionSettingsEnabled(enabled);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnVoiceInteractionSettingsEnabled(enabled);
-}
-
-void VoiceInteractionController::NotifyContextEnabled(bool enabled) {
-  if (context_enabled_.has_value() && context_enabled_.value() == enabled)
-    return;
-
-  context_enabled_ = enabled;
-  observers_.ForAllPtrs([enabled](auto* observer) {
-    observer->OnVoiceInteractionContextEnabled(enabled);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnVoiceInteractionContextEnabled(enabled);
-}
-
-void VoiceInteractionController::NotifyHotwordEnabled(bool enabled) {
-  if (hotword_enabled_.has_value() && hotword_enabled_.value() == enabled)
-    return;
-
-  hotword_enabled_ = enabled;
-  observers_.ForAllPtrs([enabled](auto* observer) {
-    observer->OnVoiceInteractionHotwordEnabled(enabled);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnVoiceInteractionHotwordEnabled(enabled);
-}
-
-void VoiceInteractionController::NotifyHotwordAlwaysOn(bool always_on) {
-  if (hotword_always_on_.has_value() && hotword_always_on_.value() == always_on)
-    return;
-
-  hotword_always_on_ = always_on;
-  observers_.ForAllPtrs([always_on](auto* observer) {
-    observer->OnVoiceInteractionHotwordAlwaysOn(always_on);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnVoiceInteractionHotwordAlwaysOn(always_on);
-}
-
-void VoiceInteractionController::NotifyConsentStatus(
-    mojom::ConsentStatus consent_status) {
-  if (consent_status_.has_value() && consent_status_.value() == consent_status)
-    return;
-
-  consent_status_ = consent_status;
-  observers_.ForAllPtrs([consent_status](auto* observer) {
-    observer->OnVoiceInteractionConsentStatusUpdated(consent_status);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnVoiceInteractionConsentStatusUpdated(consent_status);
-}
-
-void VoiceInteractionController::NotifyFeatureAllowed(
-    mojom::AssistantAllowedState state) {
-  if (allowed_state_ == state)
-    return;
-
-  allowed_state_ = state;
-  observers_.ForAllPtrs([state](auto* observer) {
-    observer->OnAssistantFeatureAllowedChanged(state);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnAssistantFeatureAllowedChanged(state);
-}
-
-void VoiceInteractionController::NotifyNotificationEnabled(bool enabled) {
-  notification_enabled_ = enabled;
-}
-
-void VoiceInteractionController::NotifyLocaleChanged(
-    const std::string& locale) {
-  if (locale_ == locale)
-    return;
-
-  locale_ = locale;
-  observers_.ForAllPtrs(
-      [locale](auto* observer) { observer->OnLocaleChanged(locale); });
-  for (auto& observer : local_observers_)
-    observer.OnLocaleChanged(locale);
-}
-
-void VoiceInteractionController::NotifyLaunchWithMicOpen(
-    bool launch_with_mic_open) {
-  launch_with_mic_open_ = launch_with_mic_open;
-}
-
-void VoiceInteractionController::NotifyArcPlayStoreEnabledChanged(
-    bool enabled) {
-  if (arc_play_store_enabled_ == enabled)
-    return;
-
-  arc_play_store_enabled_ = enabled;
-
-  observers_.ForAllPtrs([enabled](auto* observer) {
-    observer->OnArcPlayStoreEnabledChanged(enabled);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnArcPlayStoreEnabledChanged(enabled);
-}
-
-void VoiceInteractionController::NotifyLockedFullScreenStateChanged(
-    bool enabled) {
-  if (locked_full_screen_enabled_ == enabled)
-    return;
-
-  locked_full_screen_enabled_ = enabled;
-
-  observers_.ForAllPtrs([enabled](auto* observer) {
-    observer->OnLockedFullScreenStateChanged(enabled);
-  });
-  for (auto& observer : local_observers_)
-    observer.OnLockedFullScreenStateChanged(enabled);
-}
-
-void VoiceInteractionController::AddObserver(
-    mojom::VoiceInteractionObserverPtr observer) {
-  InitObserver(observer.get());
-  observers_.AddPtr(std::move(observer));
-}
-
-void VoiceInteractionController::AddLocalObserver(
-    DefaultVoiceInteractionObserver* observer) {
-  InitObserver(observer);
-  local_observers_.AddObserver(observer);
-}
-
-void VoiceInteractionController::RemoveLocalObserver(
-    DefaultVoiceInteractionObserver* observer) {
-  local_observers_.RemoveObserver(observer);
-}
-
-void VoiceInteractionController::InitObserver(
-    mojom::VoiceInteractionObserver* observer) {
-  if (voice_interaction_state_.has_value())
-    observer->OnVoiceInteractionStatusChanged(voice_interaction_state_.value());
-  if (settings_enabled_.has_value())
-    observer->OnVoiceInteractionSettingsEnabled(settings_enabled_.value());
-  if (context_enabled_.has_value())
-    observer->OnVoiceInteractionContextEnabled(context_enabled_.value());
-  if (hotword_enabled_.has_value())
-    observer->OnVoiceInteractionHotwordEnabled(hotword_enabled_.value());
-  if (consent_status_.has_value())
-    observer->OnVoiceInteractionConsentStatusUpdated(consent_status_.value());
-  if (hotword_always_on_.has_value())
-    observer->OnVoiceInteractionHotwordAlwaysOn(hotword_always_on_.value());
-  if (allowed_state_.has_value())
-    observer->OnAssistantFeatureAllowedChanged(allowed_state_.value());
-  if (locale_.has_value())
-    observer->OnLocaleChanged(locale_.value());
-  if (arc_play_store_enabled_.has_value())
-    observer->OnArcPlayStoreEnabledChanged(arc_play_store_enabled_.value());
-}
-
-void VoiceInteractionController::FlushForTesting() {
-  observers_.FlushForTesting();
-}
-
-}  // namespace ash
diff --git a/ash/voice_interaction/voice_interaction_controller.h b/ash/voice_interaction/voice_interaction_controller.h
deleted file mode 100644
index f8dcaa12..0000000
--- a/ash/voice_interaction/voice_interaction_controller.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_VOICE_INTERACTION_VOICE_INTERACTION_CONTROLLER_H_
-#define ASH_VOICE_INTERACTION_VOICE_INTERACTION_CONTROLLER_H_
-
-#include <memory>
-#include <string>
-
-#include "ash/ash_export.h"
-#include "ash/public/cpp/assistant/assistant_state_base.h"
-#include "ash/public/cpp/assistant/default_voice_interaction_observer.h"
-#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "mojo/public/cpp/bindings/interface_ptr_set.h"
-
-namespace ash {
-
-class ASH_EXPORT VoiceInteractionController
-    : public mojom::VoiceInteractionController,
-      public AssistantStateBase {
- public:
-  VoiceInteractionController();
-  ~VoiceInteractionController() override;
-
-  void BindRequest(mojom::VoiceInteractionControllerRequest request);
-
-  // ash::mojom::VoiceInteractionController:
-  void NotifyStatusChanged(mojom::VoiceInteractionState state) override;
-  void NotifySettingsEnabled(bool enabled) override;
-  void NotifyContextEnabled(bool enabled) override;
-  void NotifyHotwordEnabled(bool enabled) override;
-  void NotifyHotwordAlwaysOn(bool always_on) override;
-  void NotifyConsentStatus(mojom::ConsentStatus consent_status) override;
-  void NotifyFeatureAllowed(mojom::AssistantAllowedState state) override;
-  void NotifyNotificationEnabled(bool enabled) override;
-  void NotifyLocaleChanged(const std::string& locale) override;
-  void NotifyLaunchWithMicOpen(bool launch_with_mic_open) override;
-  void NotifyArcPlayStoreEnabledChanged(bool enabled) override;
-  void NotifyLockedFullScreenStateChanged(bool enabled) override;
-  void AddObserver(mojom::VoiceInteractionObserverPtr observer) override;
-
-  // Adding local observers in the same process.
-  void AddLocalObserver(DefaultVoiceInteractionObserver* observer);
-  void RemoveLocalObserver(DefaultVoiceInteractionObserver* observer);
-  void InitObserver(mojom::VoiceInteractionObserver* observer);
-
-  bool notification_enabled() const { return notification_enabled_; }
-
-  bool launch_with_mic_open() const { return launch_with_mic_open_; }
-
-  void FlushForTesting();
-
- private:
-  // Whether notification is enabled.
-  bool notification_enabled_ = false;
-
-  // Whether the Assistant should launch with mic open;
-  bool launch_with_mic_open_ = false;
-
-  mojo::BindingSet<mojom::VoiceInteractionController> bindings_;
-
-  mojo::InterfacePtrSet<mojom::VoiceInteractionObserver> observers_;
-
-  base::ObserverList<DefaultVoiceInteractionObserver> local_observers_;
-
-  DISALLOW_COPY_AND_ASSIGN(VoiceInteractionController);
-};
-
-}  // namespace ash
-
-#endif  // ASH_VOICE_INTERACTION_VOICE_INTERACTION_CONTROLLER_H_
diff --git a/ash/voice_interaction/voice_interaction_controller_unittest.cc b/ash/voice_interaction/voice_interaction_controller_unittest.cc
deleted file mode 100644
index fb0e922e..0000000
--- a/ash/voice_interaction/voice_interaction_controller_unittest.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/voice_interaction/voice_interaction_controller.h"
-
-#include <memory>
-#include <utility>
-
-#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
-#include "ash/shell.h"
-#include "ash/test/ash_test_base.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace ash {
-namespace {
-
-class TestVoiceInteractionObserver : public mojom::VoiceInteractionObserver {
- public:
-  TestVoiceInteractionObserver() : voice_interaction_binding_(this) {}
-
-  ~TestVoiceInteractionObserver() override = default;
-
-  // mojom::VoiceInteractionObserver overrides:
-  void OnVoiceInteractionStatusChanged(
-      mojom::VoiceInteractionState state) override {
-    state_ = state;
-  }
-  void OnVoiceInteractionSettingsEnabled(bool enabled) override {
-    settings_enabled_ = enabled;
-  }
-  void OnVoiceInteractionContextEnabled(bool enabled) override {
-    context_enabled_ = enabled;
-  }
-  void OnVoiceInteractionHotwordAlwaysOn(bool always_on) override {
-    hotword_always_on_ = always_on;
-  }
-  void OnVoiceInteractionHotwordEnabled(bool enabled) override {
-    hotword_enabled_ = enabled;
-  }
-  void OnVoiceInteractionConsentStatusUpdated(
-      mojom::ConsentStatus consent_status) override {
-    consent_status_ = consent_status;
-  }
-  void OnAssistantFeatureAllowedChanged(
-      mojom::AssistantAllowedState state) override {}
-  void OnLocaleChanged(const std::string& locale) override {}
-  void OnArcPlayStoreEnabledChanged(bool enabled) override {
-    arc_play_store_enabled_ = enabled;
-  }
-  void OnLockedFullScreenStateChanged(bool enabled) override {}
-
-  mojom::VoiceInteractionState voice_interaction_state() const {
-    return state_;
-  }
-  bool settings_enabled() const { return settings_enabled_; }
-  bool context_enabled() const { return context_enabled_; }
-  bool hotword_always_on() const { return hotword_always_on_; }
-  bool hotword_enabled() const { return hotword_enabled_; }
-  bool arc_play_store_enabled() const { return arc_play_store_enabled_; }
-  mojom::ConsentStatus consent_status() const { return consent_status_; }
-
-  void SetVoiceInteractionController(VoiceInteractionController* controller) {
-    mojom::VoiceInteractionObserverPtr ptr;
-    voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr));
-    controller->AddObserver(std::move(ptr));
-  }
-
- private:
-  mojom::VoiceInteractionState state_ = mojom::VoiceInteractionState::STOPPED;
-  bool settings_enabled_ = false;
-  bool context_enabled_ = false;
-  bool hotword_always_on_ = false;
-  bool hotword_enabled_ = false;
-  bool arc_play_store_enabled_ = false;
-  mojom::ConsentStatus consent_status_ = mojom::ConsentStatus::kUnknown;
-
-  mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestVoiceInteractionObserver);
-};
-
-class VoiceInteractionControllerTest : public AshTestBase {
- public:
-  VoiceInteractionControllerTest() = default;
-  ~VoiceInteractionControllerTest() override = default;
-
-  void SetUp() override {
-    AshTestBase::SetUp();
-
-    observer_ = std::make_unique<TestVoiceInteractionObserver>();
-    observer_->SetVoiceInteractionController(controller());
-  }
-
-  void TearDown() override {
-    observer_.reset();
-
-    AshTestBase::TearDown();
-  }
-
- protected:
-  VoiceInteractionController* controller() {
-    return Shell::Get()->voice_interaction_controller();
-  }
-
-  TestVoiceInteractionObserver* observer() { return observer_.get(); }
-
- private:
-  std::unique_ptr<TestVoiceInteractionObserver> observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(VoiceInteractionControllerTest);
-};
-
-}  // namespace
-
-TEST_F(VoiceInteractionControllerTest, NotifyStatusChanged) {
-  controller()->NotifyStatusChanged(mojom::VoiceInteractionState::RUNNING);
-  controller()->FlushForTesting();
-
-  // The cached state should be updated.
-  EXPECT_EQ(mojom::VoiceInteractionState::RUNNING,
-            controller()->voice_interaction_state());
-  // The observers should be notified.
-  EXPECT_EQ(mojom::VoiceInteractionState::RUNNING,
-            observer()->voice_interaction_state());
-}
-
-TEST_F(VoiceInteractionControllerTest, NotifySettingsEnabled) {
-  controller()->NotifySettingsEnabled(true);
-  controller()->FlushForTesting();
-  // The cached state should be updated.
-  EXPECT_TRUE(controller()->settings_enabled());
-  // The observers should be notified.
-  EXPECT_TRUE(observer()->settings_enabled());
-}
-
-TEST_F(VoiceInteractionControllerTest, NotifyContextEnabled) {
-  controller()->NotifyContextEnabled(true);
-  controller()->FlushForTesting();
-  // The observers should be notified.
-  EXPECT_TRUE(observer()->context_enabled());
-}
-
-TEST_F(VoiceInteractionControllerTest, NotifyHotwordAlwaysOn) {
-  controller()->NotifyHotwordAlwaysOn(true);
-  controller()->FlushForTesting();
-  // The observers should be notified.
-  EXPECT_TRUE(observer()->hotword_always_on());
-}
-
-TEST_F(VoiceInteractionControllerTest, NotifyHotwordEnabled) {
-  controller()->NotifyHotwordEnabled(true);
-  controller()->FlushForTesting();
-  // The observers should be notified.
-  EXPECT_TRUE(observer()->hotword_enabled());
-}
-
-TEST_F(VoiceInteractionControllerTest, NotifyConsentStatus) {
-  controller()->NotifyConsentStatus(
-      mojom::ConsentStatus::kActivityControlAccepted);
-  controller()->FlushForTesting();
-  // The cached state should be updated.
-  EXPECT_TRUE(controller()->consent_status() ==
-              mojom::ConsentStatus::kActivityControlAccepted);
-  // The observers should be notified.
-  EXPECT_TRUE(observer()->consent_status() ==
-              mojom::ConsentStatus::kActivityControlAccepted);
-}
-
-TEST_F(VoiceInteractionControllerTest, NotifyArcPlayStoreEnabledChanged) {
-  controller()->NotifyArcPlayStoreEnabledChanged(true);
-  controller()->FlushForTesting();
-  // The observers should be notified.
-  EXPECT_TRUE(observer()->arc_play_store_enabled());
-}
-
-}  // namespace ash
diff --git a/ash/wm/fullscreen_window_finder.cc b/ash/wm/fullscreen_window_finder.cc
index 18b6e0d1..d2c05756 100644
--- a/ash/wm/fullscreen_window_finder.cc
+++ b/ash/wm/fullscreen_window_finder.cc
@@ -52,7 +52,7 @@
   return nullptr;
 }
 
-// Given a |topmost_window|, returns it or one of its tranisent parents if the
+// Given a |topmost_window|, returns it or one of its transient parents if the
 // returned window is fullscreen or pinned. Otherwise, return nullptr.
 aura::Window* FindFullscreenOrPinnedWindow(aura::Window* topmost_window) {
   while (topmost_window) {
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index eba82c0..2d037f9b 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -302,7 +302,7 @@
   base::EraseIf(windows, wm::ShouldExcludeForOverview);
   // Overview windows will handle showing their transient related windows, so if
   // a window in |windows| has a transient root also in |windows|, we can remove
-  // it as the tranisent root will handle showing the window.
+  // it as the transient root will handle showing the window.
   wm::RemoveTransientDescendants(&windows);
 
   // We may want to slide the overview grid in or out in some cases, even if
@@ -507,7 +507,7 @@
 
     // Show a toast if the window cannot be snapped.
     if (!CanSnapInSplitview(active_window)) {
-      split_view_controller->ShowAppCannotSnapToast();
+      ShowAppCannotSnapToast();
       return;
     }
 
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index d7cfc1b..980ddd6 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -820,8 +820,11 @@
   // TODO(crbug.com/952315): Explore better ways to handle this splitview +
   // overview + applist case.
   Shell* shell = Shell::Get();
-  if (shell->app_list_controller() && shell->app_list_controller()->IsVisible())
+  if (shell->app_list_controller() &&
+      shell->app_list_controller()->IsVisible() &&
+      Shell::Get()->split_view_controller()->InClamshellSplitViewMode()) {
     return;
+  }
 
   if (event->type() != ui::ET_KEY_PRESSED)
     return;
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 338c7b3..24ae9ef 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -4402,6 +4402,27 @@
   EXPECT_FALSE(InOverviewSession());
 }
 
+// Test that in tablet mode, pressing tab key in overview should not crash.
+TEST_F(SplitViewOverviewSessionTest, NoCrashWhenPressTabKey) {
+  std::unique_ptr<aura::Window> window(CreateWindow(gfx::Rect(400, 400)));
+  std::unique_ptr<aura::Window> window2(CreateWindow(gfx::Rect(400, 400)));
+
+  // In overview, there should be no crash when pressing tab key.
+  ToggleOverview();
+  EXPECT_TRUE(InOverviewSession());
+  SendKey(ui::VKEY_TAB);
+  EXPECT_TRUE(InOverviewSession());
+
+  // When splitview and overview are both active, there should be no crash when
+  // pressing tab key.
+  split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT);
+  EXPECT_TRUE(InOverviewSession());
+  EXPECT_TRUE(split_view_controller()->InSplitViewMode());
+
+  SendKey(ui::VKEY_TAB);
+  EXPECT_TRUE(InOverviewSession());
+}
+
 // Test the split view and overview functionalities in clamshell mode. Split
 // view is only active when overview is active in clamshell mode.
 class SplitViewOverviewSessionInClamshellTest
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index 6aa4928..303f7c9c 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -246,7 +246,7 @@
   } else {
     split_view_controller_->EndSplitView();
     overview_session_->SelectWindow(item_);
-    split_view_controller_->ShowAppCannotSnapToast();
+    ShowAppCannotSnapToast();
   }
   current_drag_behavior_ = DragBehavior::kNoDrag;
   UnpauseOcclusionTracker();
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc
index 3c0157aa..b7f13e1 100644
--- a/ash/wm/overview/scoped_overview_transform_window.cc
+++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -27,6 +27,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_observer.h"
@@ -177,37 +178,39 @@
       original_mask_layer_(window_->layer()->layer_mask_layer()),
       weak_ptr_factory_(this) {
   type_ = GetWindowDimensionsType(window);
-  original_event_targeting_policy_ = window_->event_targeting_policy();
-  window_->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
-  window_->SetProperty(kIsShowingInOverviewKey, true);
 
-  // Hide transient children which have been specified to be hidden in overview
-  // mode.
   std::vector<aura::Window*> transient_children_to_hide;
   for (auto* transient : wm::GetTransientTreeIterator(window)) {
-    if (transient == window)
-      continue;
-
-    if (transient->GetProperty(kHideInOverviewKey))
-      transient_children_to_hide.push_back(transient);
-
+    targeting_policy_map_[transient] = transient->event_targeting_policy();
+    transient->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
     transient->SetProperty(kIsShowingInOverviewKey, true);
+
+    // Hide transient children which have been specified to be hidden in
+    // overview mode.
+    if (transient != window && transient->GetProperty(kHideInOverviewKey))
+      transient_children_to_hide.push_back(transient);
   }
 
   if (!transient_children_to_hide.empty()) {
     hidden_transient_children_ = std::make_unique<ScopedOverviewHideWindows>(
         std::move(transient_children_to_hide), /*forced_hidden=*/true);
   }
+
+  aura::client::GetTransientWindowClient()->AddObserver(this);
 }
 
 ScopedOverviewTransformWindow::~ScopedOverviewTransformWindow() {
-  for (auto* transient : wm::GetTransientTreeIterator(window_))
+  for (auto* transient : wm::GetTransientTreeIterator(window_)) {
     transient->ClearProperty(kIsShowingInOverviewKey);
-  DCHECK(!window_->GetProperty(kIsShowingInOverviewKey));
+    DCHECK(targeting_policy_map_.contains(transient));
+    auto it = targeting_policy_map_.find(transient);
+    transient->SetEventTargetingPolicy(it->second);
+    targeting_policy_map_.erase(it);
+  }
 
-  window_->SetEventTargetingPolicy(original_event_targeting_policy_);
   UpdateMask(/*show=*/false);
   StopObservingImplicitAnimations();
+  aura::client::GetTransientWindowClient()->RemoveObserver(this);
 }
 
 // static
@@ -437,17 +440,6 @@
   }
 }
 
-void ScopedOverviewTransformWindow::CloseWidget() {
-  aura::Window* parent_window = ::wm::GetTransientRoot(window_);
-  if (parent_window)
-    wm::CloseWidgetForWindow(parent_window);
-}
-
-// static
-void ScopedOverviewTransformWindow::SetImmediateCloseForTests() {
-  immediate_close_for_tests = true;
-}
-
 void ScopedOverviewTransformWindow::EnsureVisible() {
   original_opacity_ = 1.f;
 }
@@ -502,10 +494,45 @@
   overview_item_->OnDragAnimationCompleted();
 }
 
+void ScopedOverviewTransformWindow::OnTransientChildWindowAdded(
+    aura::Window* parent,
+    aura::Window* transient_child) {
+  if (parent != window_ && !::wm::HasTransientAncestor(parent, window_))
+    return;
+
+  DCHECK(!targeting_policy_map_.contains(transient_child));
+  targeting_policy_map_[transient_child] =
+      transient_child->event_targeting_policy();
+  transient_child->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
+}
+
+void ScopedOverviewTransformWindow::OnTransientChildWindowRemoved(
+    aura::Window* parent,
+    aura::Window* transient_child) {
+  if (parent != window_ && !::wm::HasTransientAncestor(parent, window_))
+    return;
+
+  DCHECK(targeting_policy_map_.contains(transient_child));
+  auto it = targeting_policy_map_.find(transient_child);
+  transient_child->SetEventTargetingPolicy(it->second);
+  targeting_policy_map_.erase(it);
+}
+
 gfx::Rect ScopedOverviewTransformWindow::GetMaskBoundsForTesting() const {
   if (!mask_)
     return gfx::Rect();
   return mask_->layer()->bounds();
 }
 
+void ScopedOverviewTransformWindow::CloseWidget() {
+  aura::Window* parent_window = ::wm::GetTransientRoot(window_);
+  if (parent_window)
+    wm::CloseWidgetForWindow(parent_window);
+}
+
+// static
+void ScopedOverviewTransformWindow::SetImmediateCloseForTests() {
+  immediate_close_for_tests = true;
+}
+
 }  // namespace ash
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h
index d414285..3119e2b 100644
--- a/ash/wm/overview/scoped_overview_transform_window.h
+++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "ui/aura/client/transient_window_client_observer.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -39,7 +40,8 @@
 // fit in certain bounds. The window's state is restored when this object is
 // destroyed.
 class ASH_EXPORT ScopedOverviewTransformWindow
-    : public ui::ImplicitAnimationObserver {
+    : public ui::ImplicitAnimationObserver,
+      public aura::client::TransientWindowClientObserver {
  public:
   // Overview windows have certain properties if their aspect ratio exceedes a
   // threshold. This enum keeps track of which category the window falls into,
@@ -155,6 +157,12 @@
   void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override;
   void OnImplicitAnimationsCompleted() override;
 
+  // aura::client::TransientWindowClientObserver:
+  void OnTransientChildWindowAdded(aura::Window* parent,
+                                   aura::Window* transient_child) override;
+  void OnTransientChildWindowRemoved(aura::Window* parent,
+                                     aura::Window* transient_child) override;
+
   aura::Window* window() const { return window_; }
 
   GridWindowFillMode type() const { return type_; }
@@ -191,9 +199,6 @@
   // The original opacity of the window before entering overview mode.
   float original_opacity_;
 
-  // For the duration of this object |window_| event targeting policy will be
-  // sent to NONE. Store the original so we can change it back when destroying
-  // this object.
   aura::EventTargetingPolicy original_event_targeting_policy_;
 
   // Specifies how the window is laid out in the grid.
@@ -213,6 +218,12 @@
   // while in overview.
   std::unique_ptr<WindowMask> mask_;
 
+  // For the duration of this object |window_| and its transient childrens'
+  // event targeting policy will be sent to NONE. Store the originals so we can
+  // change it back when destroying |this|.
+  base::flat_map<aura::Window*, aura::EventTargetingPolicy>
+      targeting_policy_map_;
+
   // The original mask layer of the window before entering overview mode.
   ui::Layer* original_mask_layer_ = nullptr;
 
diff --git a/ash/wm/overview/scoped_overview_transform_window_unittest.cc b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
index 9e677d04..b4e25bb 100644
--- a/ash/wm/overview/scoped_overview_transform_window_unittest.cc
+++ b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
@@ -281,6 +281,62 @@
   EXPECT_TRUE(child2->transform().IsIdentity());
 }
 
+// Tests that the event targeting policies of a given window and transient
+// descendants gets set as expected.
+TEST_F(ScopedOverviewTransformWindowTest, EventTargetingPolicy) {
+  using etp = aura::EventTargetingPolicy;
+
+  // Helper for creating popups that will be transients for testing.
+  auto create_popup = [this] {
+    std::unique_ptr<aura::Window> popup =
+        CreateTestWindow(gfx::Rect(10, 10), aura::client::WINDOW_TYPE_POPUP);
+    popup->SetEventTargetingPolicy(etp::kTargetAndDescendants);
+    return popup;
+  };
+
+  auto window = CreateTestWindow(gfx::Rect(200, 200));
+  window->SetEventTargetingPolicy(etp::kTargetAndDescendants);
+
+  auto transient = create_popup();
+  auto transient1 = create_popup();
+  auto transient2 = create_popup();
+  ::wm::AddTransientChild(window.get(), transient.get());
+
+  {
+    // Tests that after creating the scoped object, the window and its current
+    // transient child have |kNone| targeting policy.
+    ScopedOverviewTransformWindow scoped_window(nullptr, window.get());
+    EXPECT_EQ(etp::kNone, window->event_targeting_policy());
+    EXPECT_EQ(etp::kNone, transient->event_targeting_policy());
+
+    // Tests that after adding transient children, one to the window itself and
+    // one to the current transient child, they will both have |kNone| targeting
+    // policy.
+    ::wm::AddTransientChild(window.get(), transient1.get());
+    ::wm::AddTransientChild(transient.get(), transient2.get());
+    EXPECT_EQ(etp::kNone, transient1->event_targeting_policy());
+    EXPECT_EQ(etp::kNone, transient2->event_targeting_policy());
+
+    // Tests that adding a transient child which does not have |window| as its
+    // descendant does not have its targeting policy altered.
+    auto window2 = CreateTestWindow(gfx::Rect(200, 200));
+    auto transient3 = create_popup();
+    ::wm::AddTransientChild(window2.get(), transient3.get());
+    EXPECT_EQ(etp::kTargetAndDescendants, transient3->event_targeting_policy());
+
+    // Tests that removing a transient child from |window| will reset its
+    // targeting policy.
+    ::wm::RemoveTransientChild(window.get(), transient1.get());
+    EXPECT_EQ(etp::kTargetAndDescendants, transient1->event_targeting_policy());
+  }
+
+  // Tests that when the scoped object is destroyed, the targeting policies all
+  // get reset.
+  EXPECT_EQ(etp::kTargetAndDescendants, window->event_targeting_policy());
+  EXPECT_EQ(etp::kTargetAndDescendants, transient->event_targeting_policy());
+  EXPECT_EQ(etp::kTargetAndDescendants, transient2->event_targeting_policy());
+}
+
 class ScopedOverviewTransformWindowWithMaskTest
     : public ScopedOverviewTransformWindowTest {
  public:
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index ab685ca3..75119626 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -15,9 +15,6 @@
 #include "ash/screen_util.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "ash/system/toast/toast_data.h"
-#include "ash/system/toast/toast_manager.h"
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -44,7 +41,6 @@
 #include "ui/aura/window_delegate.h"
 #include "ui/base/class_property.h"
 #include "ui/base/hit_test.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/animation/tween.h"
@@ -80,10 +76,6 @@
 // tablet_mode_window_drag_delegate.cc.
 constexpr int kDividerSnapDurationMs = 300;
 
-// Toast data.
-constexpr char kAppCannotSnapToastId[] = "split_view_app_cannot_snap";
-constexpr int kAppCannotSnapToastDurationMs = 2500;
-
 // Histogram names that record presentation time of resize operation with
 // following conditions, a) single snapped window, empty overview, b) two
 // snapped windows, c) single snapped window and non empty overview.
@@ -269,7 +261,9 @@
         CurrentValueBetween(starting_position_, ending_position_);
     split_view_controller_->NotifyDividerPositionChanged();
     split_view_controller_->UpdateSnappedWindowsAndDividerBounds();
-    split_view_controller_->SetWindowsTransformDuringResizing();
+    // Updating the window may stop animation.
+    if (is_animating())
+      split_view_controller_->SetWindowsTransformDuringResizing();
   }
 
   SplitViewController* split_view_controller_;
@@ -667,14 +661,6 @@
   }
 }
 
-void SplitViewController::ShowAppCannotSnapToast() {
-  ash::ToastData toast(
-      kAppCannotSnapToastId,
-      l10n_util::GetStringUTF16(IDS_ASH_SPLIT_VIEW_CANNOT_SNAP),
-      kAppCannotSnapToastDurationMs, base::Optional<base::string16>());
-  ash::Shell::Get()->toast_manager()->Show(toast);
-}
-
 void SplitViewController::EndSplitView(EndReason end_reason) {
   if (!InSplitViewMode())
     return;
@@ -688,8 +674,11 @@
   const bool is_divider_animating = IsDividerAnimating();
   if (is_resizing_ || is_divider_animating) {
     is_resizing_ = false;
-    if (is_divider_animating)
-      StopAndShoveAnimatedDivider();
+    if (is_divider_animating) {
+      // Don't call StopAndShoveAnimatedDivider as it will call observers.
+      divider_snap_animation_->Stop();
+      divider_position_ = divider_snap_animation_->ending_position();
+    }
     EndResizeImpl();
   }
 
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 9082203f..7a4a801 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -141,10 +141,6 @@
   void Resize(const gfx::Point& location_in_screen);
   void EndResize(const gfx::Point& location_in_screen);
 
-  // Displays a toast notifying users the application selected for split view is
-  // not compatible.
-  void ShowAppCannotSnapToast();
-
   // Ends the split view mode.
   void EndSplitView(EndReason end_reason = EndReason::kNormal);
 
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index 16160ee4..3f73975b 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -10,12 +10,16 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/toast/toast_data.h"
+#include "ash/system/toast/toast_manager.h"
 #include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "base/command_line.h"
 #include "ui/aura/window_delegate.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animator.h"
@@ -52,6 +56,10 @@
 constexpr float kHighlightOpacity = 0.3f;
 constexpr float kPreviewAreaHighlightOpacity = 0.18f;
 
+// Toast data.
+constexpr char kAppCannotSnapToastId[] = "split_view_app_cannot_snap";
+constexpr int kAppCannotSnapToastDurationMs = 2500;
+
 // Gets the duration, tween type and delay before animation based on |type|.
 void GetAnimationValuesForType(
     SplitviewAnimationType type,
@@ -268,6 +276,13 @@
   return true;
 }
 
+void ShowAppCannotSnapToast() {
+  ash::Shell::Get()->toast_manager()->Show(ash::ToastData(
+      kAppCannotSnapToastId,
+      l10n_util::GetStringUTF16(IDS_ASH_SPLIT_VIEW_CANNOT_SNAP),
+      kAppCannotSnapToastDurationMs, base::Optional<base::string16>()));
+}
+
 bool IsPhysicalLeftOrTop(SplitViewController::SnapPosition position) {
   DCHECK_NE(SplitViewController::NONE, position);
   return position == (IsCurrentScreenOrientationPrimary()
diff --git a/ash/wm/splitview/split_view_utils.h b/ash/wm/splitview/split_view_utils.h
index fc276f1..4012a6a 100644
--- a/ash/wm/splitview/split_view_utils.h
+++ b/ash/wm/splitview/split_view_utils.h
@@ -91,6 +91,10 @@
 // tablet mode.
 ASH_EXPORT bool CanSnapInSplitview(aura::Window* window);
 
+// Displays a toast notifying users the application selected for split view is
+// not compatible.
+ASH_EXPORT void ShowAppCannotSnapToast();
+
 ASH_EXPORT bool IsPhysicalLeftOrTop(SplitViewController::SnapPosition position);
 
 }  // namespace ash
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc
index b190d192..3eb695a7 100644
--- a/ash/wm/window_cycle_controller.cc
+++ b/ash/wm/window_cycle_controller.cc
@@ -60,7 +60,7 @@
       Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks);
   // Window cycle list windows will handle showing their transient related
   // windows, so if a window in |window_list| has a transient root also in
-  // |window_list|, we can remove it as the tranisent root will handle showing
+  // |window_list|, we can remove it as the transient root will handle showing
   // the window.
   wm::RemoveTransientDescendants(&window_list);
 
diff --git a/base/allocator/partition_allocator/address_space_randomization_unittest.cc b/base/allocator/partition_allocator/address_space_randomization_unittest.cc
index 3fe46ba6..395567a 100644
--- a/base/allocator/partition_allocator/address_space_randomization_unittest.cc
+++ b/base/allocator/partition_allocator/address_space_randomization_unittest.cc
@@ -180,9 +180,10 @@
       // Chi squared analysis for k = 2 (2, states: same/not-same) and one
       // degree of freedom (k - 1).
       double chi_squared = ChiSquared(m, kRepeats);
-      // For 1 degree of freedom this corresponds to 1 in a million.  We are
-      // running ~8000 tests, so that would be surprising.
-      CHECK_GE(24, chi_squared);
+      // For k=2 probability of Chi^2 < 35 is p=3.338e-9. This condition is
+      // tested ~19000 times, so probability of it failing randomly per one
+      // base_unittests run is (1 - (1 - p) ^ 19000) ~= 6e-5.
+      CHECK_LE(chi_squared, 35.0);
       // If the predictor bit is a fixed 0 or 1 then it makes no sense to
       // repeat the test with a different age.
       if (predictor_bit < 0)
@@ -191,9 +192,6 @@
   }
 }
 
-// TODO(crbug.com/811881): These are flaky on Fuchsia
-#if !defined(OS_FUCHSIA)
-
 // Tests are fairly slow, so give each random bit its own test.
 #define TEST_RANDOM_BIT(BIT)                                        \
   TEST(AddressSpaceRandomizationTest, RandomBitCorrelations##BIT) { \
@@ -242,8 +240,6 @@
 // No platforms have more than 48 address bits.
 #endif  // defined(ARCH_CPU_64_BITS)
 
-#endif  // defined(OS_FUCHSIA)
-
 #undef TEST_RANDOM_BIT
 
 }  // namespace base
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index 45f78a2..f1ad6880 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -1260,7 +1260,6 @@
 
   // GetHFSDecomposedForm() returns an empty string in an error case.
   if (hfs1.empty() || hfs2.empty()) {
-    NOTREACHED();
     ScopedCFTypeRef<CFStringRef> cfstring1(
         CFStringCreateWithBytesNoCopy(
             NULL,
@@ -1277,6 +1276,20 @@
             kCFStringEncodingUTF8,
             false,
             kCFAllocatorNull));
+    // If neither GetHFSDecomposedForm nor CFStringCreateWithBytesNoCopy
+    // succeed, fall back to strcmp. This can occur when the input string is
+    // invalid UTF-8.
+    if (!cfstring1 || !cfstring2) {
+      int comparison =
+          memcmp(string1.as_string().c_str(), string2.as_string().c_str(),
+                 std::min(string1.length(), string2.length()));
+      if (comparison < 0)
+        return -1;
+      if (comparison > 0)
+        return 1;
+      return 0;
+    }
+
     return CFStringCompare(cfstring1,
                            cfstring2,
                            kCFCompareCaseInsensitive);
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc
index afe70916..c3cbc9a 100644
--- a/base/files/file_path_unittest.cc
+++ b/base/files/file_path_unittest.cc
@@ -1317,6 +1317,17 @@
     EXPECT_TRUE(observed.empty());
   }
 }
+
+TEST_F(FilePathTest, CompareIgnoreCaseWithInvalidInput) {
+  const FilePath::CharType* cases[] = {
+      FPL("\xc3\x28"),         FPL("\xe2\x82\x28"),     FPL("\xe2\x28\xa1"),
+      FPL("\xf0\x28\x8c\xbc"), FPL("\xf0\x28\x8c\x28"),
+  };
+  for (auto* invalid_input : cases) {
+    // All example inputs will be greater than the string "fixed".
+    EXPECT_EQ(FilePath::CompareIgnoreCase(invalid_input, FPL("fixed")), 1);
+  }
+}
 #endif
 
 }  // namespace base
diff --git a/base/i18n/time_formatting.h b/base/i18n/time_formatting.h
index dfbfeea6..64b09964 100644
--- a/base/i18n/time_formatting.h
+++ b/base/i18n/time_formatting.h
@@ -47,10 +47,6 @@
   DATE_FORMAT_MONTH_WEEKDAY_DAY,
 };
 
-// TODO(derat@chromium.org): Update all of these functions to return boolean
-// "success" values and use out-params for formatted strings:
-// http://crbug.com/698802
-
 // Returns the time of day, e.g., "3:07 PM".
 BASE_I18N_EXPORT string16 TimeFormatTimeOfDay(const Time& time);
 
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
index 16bb6fe..515cf6c 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cmath>
+#include <memory>
 #include <utility>
 
 #include "base/allocator/allocator_shim.h"
@@ -319,9 +320,8 @@
   CHECK_EQ(nullptr, instance_);
   instance_ = this;
   Init();
-  auto sampled_addresses = std::make_unique<LockFreeAddressHashSet>(64);
-  g_sampled_addresses_set = sampled_addresses.get();
-  sampled_addresses_stack_.push_back(std::move(sampled_addresses));
+  auto* sampled_addresses = new LockFreeAddressHashSet(64);
+  g_sampled_addresses_set.store(sampled_addresses, std::memory_order_release);
 }
 
 // static
@@ -509,16 +509,15 @@
       std::make_unique<LockFreeAddressHashSet>(current_set.buckets_count() * 2);
   new_set->Copy(current_set);
   // Atomically switch all the new readers to the new set.
-  g_sampled_addresses_set = new_set.get();
-  // We still have to keep all the old maps alive to resolve the theoretical
-  // race with readers in |RecordFree| that have already obtained the map,
+  g_sampled_addresses_set.store(new_set.release(), std::memory_order_release);
+  // We leak the older set because we still have to keep all the old maps alive
+  // as there might be reader threads that have already obtained the map,
   // but haven't yet managed to access it.
-  sampled_addresses_stack_.push_back(std::move(new_set));
 }
 
 // static
 LockFreeAddressHashSet& PoissonAllocationSampler::sampled_addresses_set() {
-  return *g_sampled_addresses_set.load(std::memory_order_relaxed);
+  return *g_sampled_addresses_set.load(std::memory_order_acquire);
 }
 
 // static
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.h b/base/sampling_heap_profiler/poisson_allocation_sampler.h
index 7709e02..f0751e4 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.h
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.h
@@ -5,7 +5,6 @@
 #ifndef BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_
 #define BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_
 
-#include <memory>
 #include <vector>
 
 #include "base/base_export.h"
@@ -110,7 +109,6 @@
   void BalanceAddressesHashSet();
 
   Lock mutex_;
-  std::vector<std::unique_ptr<LockFreeAddressHashSet>> sampled_addresses_stack_;
   std::vector<SamplesObserver*> observers_;
 
   static PoissonAllocationSampler* instance_;
diff --git a/build/android/gyp/main_dex_list.py b/build/android/gyp/main_dex_list.py
index 520be39..2435859 100755
--- a/build/android/gyp/main_dex_list.py
+++ b/build/android/gyp/main_dex_list.py
@@ -32,8 +32,8 @@
   parser.add_argument('--inputs',
                       help='JARs for which a main dex list should be '
                            'generated.')
-  parser.add_argument(
-      '--r8-path', required=True, help='Path to the r8 executable.')
+  parser.add_argument('--proguard-path', required=True,
+                      help='Path to the proguard executable.')
   parser.add_argument('--negative-main-dex-globs',
       help='GN-list of globs of .class names (e.g. org/chromium/foo/Bar.class) '
            'that will fail the build if they match files in the main dex.')
@@ -55,24 +55,13 @@
         args.negative_main_dex_globs)
 
   proguard_cmd = [
-      'java',
-      '-jar',
-      args.r8_path,
-      '--classfile',
-      '--lib',
-      args.shrinked_android_path,
+    'java', '-jar', args.proguard_path,
+    '-forceprocessing',
+    '-dontwarn', '-dontoptimize', '-dontobfuscate', '-dontpreverify',
+    '-libraryjars', args.shrinked_android_path,
   ]
-
   for m in args.main_dex_rules_paths:
-    proguard_cmd.extend(['--pg-conf', m])
-
-  proguard_flags = [
-      '-forceprocessing',
-      '-dontwarn',
-      '-dontoptimize',
-      '-dontobfuscate',
-      '-dontpreverify',
-  ]
+    proguard_cmd.extend(['-include', m])
 
   main_dex_list_cmd = [
     'java', '-cp', args.dx_path,
@@ -94,24 +83,17 @@
     proguard_cmd,
     main_dex_list_cmd,
   ]
-
   if args.negative_main_dex_globs:
     input_strings += args.negative_main_dex_globs
-    for glob in args.negative_main_dex_globs:
-      # Globs come with 1 asterix, but we want 2 to match subpackages.
-      proguard_flags.append('-checkdiscard class ' +
-                            glob.replace('*', '**').replace('/', '.'))
 
   output_paths = [
     args.main_dex_list_path,
   ]
 
-  def _LineLengthHelperForOnStaleMd5():
-    _OnStaleMd5(proguard_cmd, proguard_flags, main_dex_list_cmd, args.paths,
-                args.main_dex_list_path)
-
   build_utils.CallAndWriteDepfileIfStale(
-      _LineLengthHelperForOnStaleMd5,
+      lambda: _OnStaleMd5(proguard_cmd, main_dex_list_cmd, args.paths,
+                          args.main_dex_list_path,
+                          args.negative_main_dex_globs),
       args,
       input_paths=input_paths,
       input_strings=input_strings,
@@ -122,22 +104,38 @@
   return 0
 
 
-def _OnStaleMd5(proguard_cmd, proguard_flags, main_dex_list_cmd, paths,
-                main_dex_list_path):
+def _CheckForUnwanted(kept_classes, proguard_cmd, negative_main_dex_globs):
+  # Check if ProGuard kept any unwanted classes.
+  found_unwanted_classes = sorted(
+      p for p in kept_classes
+      if build_utils.MatchesGlob(p, negative_main_dex_globs))
+
+  if found_unwanted_classes:
+    first_class = found_unwanted_classes[0].replace(
+        '.class', '').replace('/', '.')
+    proguard_cmd += ['-whyareyoukeeping', 'class', first_class, '{}']
+    output = build_utils.CheckOutput(
+        proguard_cmd, print_stderr=False,
+        stdout_filter=proguard_util.ProguardOutputFilter())
+    raise Exception(
+        ('Found classes that should not be in the main dex:\n    {}\n\n'
+         'Here is the -whyareyoukeeping output for {}: \n{}').format(
+             '\n    '.join(found_unwanted_classes), first_class, output))
+
+
+def _OnStaleMd5(proguard_cmd, main_dex_list_cmd, paths, main_dex_list_path,
+                negative_main_dex_globs):
+  paths_arg = ':'.join(paths)
   main_dex_list = ''
   try:
     with tempfile.NamedTemporaryFile(suffix='.jar') as temp_jar:
       # Step 1: Use ProGuard to find all @MainDex code, and all code reachable
       # from @MainDex code (recursive).
-      proguard_cmd += ['--output', temp_jar.name]
-      with tempfile.NamedTemporaryFile() as proguard_flags_file:
-        for flag in proguard_flags:
-          proguard_flags_file.write(flag + '\n')
-        proguard_flags_file.flush()
-        proguard_cmd += ['--pg-conf', proguard_flags_file.name]
-        for injar in paths:
-          proguard_cmd.append(injar)
-        build_utils.CheckOutput(proguard_cmd, print_stderr=False)
+      proguard_cmd += [
+        '-injars', paths_arg,
+        '-outjars', temp_jar.name
+      ]
+      build_utils.CheckOutput(proguard_cmd, print_stderr=False)
 
       # Record the classes kept by ProGuard. Not used by the build, but useful
       # for debugging what classes are kept by ProGuard vs. MainDexListBuilder.
@@ -146,9 +144,18 @@
       with open(main_dex_list_path + '.partial', 'w') as f:
         f.write('\n'.join(kept_classes) + '\n')
 
+      if negative_main_dex_globs:
+        # Perform assertions before MainDexListBuilder because:
+        # a) MainDexListBuilder is not recursive, so being included by it isn't
+        #    a huge deal.
+        # b) Errors are much more actionable.
+        _CheckForUnwanted(kept_classes, proguard_cmd, negative_main_dex_globs)
+
       # Step 2: Expand inclusion list to all classes referenced by the .class
       # files of kept classes (non-recursive).
-      main_dex_list_cmd += [temp_jar.name, ':'.join(paths)]
+      main_dex_list_cmd += [
+        temp_jar.name, paths_arg
+      ]
       main_dex_list = build_utils.CheckOutput(main_dex_list_cmd)
 
   except build_utils.CalledProcessError as e:
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index aff8874..010202bd 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1302,11 +1302,18 @@
           # http://crbug.com/725224. Fix for bots running out of memory.
           pool = "//build/toolchain:link_pool($default_toolchain)"
 
+          if (defined(invoker.proguard_jar_path)) {
+            _proguard_jar_path = invoker.proguard_jar_path
+          } else {
+            _proguard_jar_path = _default_proguard_jar_path
+          }
+
           _shrinked_android = "$android_sdk_build_tools/lib/shrinkedAndroid.jar"
           _dx = "$android_sdk_build_tools/lib/dx.jar"
           inputs = [
             _main_dex_rules,
             _dx,
+            _proguard_jar_path,
             _shrinked_android,
           ]
 
@@ -1325,8 +1332,8 @@
             rebase_path(_main_dex_list_path, root_build_dir),
             "--main-dex-rules-path",
             rebase_path(_main_dex_rules, root_build_dir),
-            "--r8-path",
-            rebase_path("//third_party/r8/lib/r8.jar", root_build_dir),
+            "--proguard-path",
+            rebase_path(_proguard_jar_path, root_build_dir),
           ]
 
           if (defined(invoker.extra_main_dex_proguard_config)) {
@@ -1351,11 +1358,11 @@
           if (defined(invoker.input_jar_classpath)) {
             inputs += [ invoker.build_config ]
             args += [ "--inputs=@FileArg(${invoker.input_jar_classpath})" ]
-          } else {
-            inputs += _dexing_jars
-            if (_dexing_jars != []) {
-              args += rebase_path(_dexing_jars, root_build_dir)
-            }
+          }
+
+          inputs += _dexing_jars
+          if (_dexing_jars != []) {
+            args += rebase_path(_dexing_jars, root_build_dir)
           }
         }
       }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 9508d9c..e3c810e 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -31,7 +31,7 @@
   _sanitizer_runtimes = [ "$clang_base_path/lib/clang/$clang_version/lib/linux/libclang_rt.ubsan_standalone-$_sanitizer_arch-android.so" ]
 }
 
-if (is_hwasan) {
+if (is_hwasan && !hwasan_platform) {
   _sanitizer_runtimes = [ "$clang_base_path/lib/clang/$clang_version/lib/linux/libclang_rt.hwasan-$_sanitizer_arch-android.so" ]
 }
 
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index 993d8e7..d33e818b 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -436,6 +436,9 @@
 config("hwasan_flags") {
   if (is_hwasan) {
     cflags = [ "-fsanitize=hwaddress" ]
+    if (hwasan_platform) {
+      cflags += [ "-fsanitize-hwaddress-abi=platform" ]
+    }
   }
 }
 
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index 8cff7271..2ac235b3 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -16,6 +16,10 @@
   # See http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
   is_hwasan = false
 
+  # Specify whether to target the platform's copy of the HWASan runtime,
+  # rather than one bundled with the application.
+  hwasan_platform = false
+
   # Compile for Leak Sanitizer to find leaks.
   is_lsan = false
 
@@ -123,6 +127,7 @@
   is_asan = false
   is_cfi = false
   is_hwasan = false
+  hwasan_platform = false
   is_lsan = false
   is_msan = false
   is_tsan = false
@@ -245,6 +250,9 @@
         "Chromium mac_clang_x64 toolchain on iOS distribution. Please set " +
         "the argument value to false.")
 
+assert(!hwasan_platform || is_hwasan,
+       "hwasan_platform requires is_hwasan to be set")
+
 # Use these lists of configs to disable instrumenting code that is part of a
 # fuzzer, but which isn't being targeted (such as libprotobuf-mutator, *.pb.cc
 # and libprotobuf when they are built as part of a proto fuzzer). Adding or
diff --git a/build/partitioned_shared_library.gni b/build/partitioned_shared_library.gni
index 33b7f1c..81e372f 100644
--- a/build/partitioned_shared_library.gni
+++ b/build/partitioned_shared_library.gni
@@ -27,18 +27,18 @@
 # The template instantiates targets for the base library, as well as each
 # specified partition, based on the root target name.  Example:
 #
-#   - monochrome_base       (base library)
-#   - monochrome_base_foo   (partition library for feature 'foo')
-#   - monochrome_base_bar   (partition library for feature 'bar')
+#   - monochrome            (base library)
+#   - monochrome_foo        (partition library for feature 'foo')
+#   - monochrome_bar        (partition library for feature 'bar')
 #
 # The base library is placed in the root output directory, but additional
 # feature libraries are placed in a subdirectory named according to the base
 # library.  This avoids name collisions, since feature library names are not
 # sensitive to the base library to which they are paired.  Example:
 #
-#   - out/libmonochrome_base.so
-#   - out/monochrome_base_partitions/libfoo.so
-#   - out/monochrome_base_partitions/libbar.so
+#   - out/libmonochrome.so
+#   - out/monochrome_partitions/libfoo.so
+#   - out/monochrome_partitions/libbar.so
 #
 # This template uses shared_library's default configurations.
 #
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 7b277dc4..1f6534b 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -6,7 +6,6 @@
 import("//build/config/compiler/compiler.gni")
 import("//build/config/compiler/pgo/pgo.gni")
 import("//build/config/features.gni")
-import("//build/config/linux/pangocairo/pangocairo.gni")
 import("//build/config/locales.gni")
 import("//build/config/sanitizers/sanitizers.gni")
 import("//build/config/ui.gni")
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 1eec119..6ab78ed 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -425,7 +425,7 @@
     "//components/signin/core/browser:signin_enums_javagen",
     "//components/supervised_user_error_page:enums_srcjar",
     "//components/ui_metrics:ui_metrics_enums_java",
-    "//chrome/browser/notifications/scheduler:jni_enums",
+    "//chrome/browser/notifications/scheduler/public:jni_enums",
     "//chrome/browser/ui:tab_model_enums_java",
     "//net:effective_connection_type_java",
     ":vr_build_config",
@@ -1147,44 +1147,26 @@
   }
 }
 
-# This template creates a native library for Chrome's APK or bundle.
-template("libchrome_apk_or_bundle_tmpl") {
-  chrome_common_shared_library(target_name) {
-    forward_variables_from(invoker, "*", [ "is_bundle" ])
-    sources = [
-      "../browser/android/chrome_entry_point.cc",
-      chrome_jni_registration_header,
-    ]
-    deps = [
-      ":chrome_jni_registration($default_toolchain)",
-    ]
+# Chrome APK's native library.
+chrome_common_shared_library("libchrome") {
+  sources = [
+    "../browser/android/chrome_entry_point.cc",
+    chrome_jni_registration_header,
+  ]
+  deps = [
+    ":chrome_jni_registration($default_toolchain)",
+  ]
 
-    # Include appropriate factories for native feature modules if necessary.
-    if (enable_vr) {
-      if (defined(invoker.is_bundle) && invoker.is_bundle) {
-        if (modularize_vr_native) {
-          deps += [ "//chrome/browser/android/vr:ui_module_factory" ]
-        } else {
-          deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ]
-        }
-      } else {
-        deps += [ "//chrome/browser/android/vr:ui_default_factory" ]
-      }
+  # Include appropriate factories for native feature modules if necessary.
+  if (enable_vr) {
+    if (modularize_vr_native) {
+      deps += [ "//chrome/browser/android/vr:ui_module_factory" ]
+    } else {
+      deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ]
     }
   }
 }
 
-# Chrome APK's native library.
-libchrome_apk_or_bundle_tmpl("libchrome") {
-  is_bundle = false
-}
-
-# Chrome bundle's base module native library. This is currently the same as the
-# APK version, but will diverge when code moves into feature modules.
-libchrome_apk_or_bundle_tmpl("libchrome_base") {
-  is_bundle = true
-}
-
 chrome_common_shared_library("libchromefortest") {
   testonly = true
   sources = [
@@ -1236,25 +1218,18 @@
   #    is_monochrome: If true, generate Monochrome targets rather than Chrome.
   #    is_trichrome: Optionally generate Trichrome targets that use monochrome
   #      library targets but don't include webview resources.
-  #    is_bundle: If true, generate resources for bundles rather than APK.
   template("resource_packaging") {
     _is_monochrome = invoker.is_monochrome
-    _is_bundle = invoker.is_bundle
     _is_trichrome = defined(invoker.is_trichrome) && invoker.is_trichrome
 
     if (_is_trichrome) {
-      _type = "trichrome_chrome"
+      _variant = "trichrome_chrome"
     } else if (_is_monochrome) {
-      _type = "monochrome"
+      _variant = "monochrome"
     } else {
-      _type = "chrome"
+      _variant = "chrome"
     }
-    if (_is_bundle) {
-      _output_type = "bundle"
-    } else {
-      _output_type = "apk"
-    }
-    _variant = "${_type}_${_output_type}"
+    _variant += "_apk"
 
     if (enable_resource_whitelist_generation) {
       if (_is_trichrome || _is_monochrome) {
@@ -1271,13 +1246,8 @@
       } else {
         _target_prefix = "lib"
       }
-      if (_is_bundle) {
-        _suffix = bundle_library_suffix
-      } else {
-        _suffix = ""
-      }
-      _lib_path = "/lib.unstripped/lib" + _lib + _suffix + shlib_extension
-      _lib_target = _target_prefix + _lib + _suffix
+      _lib_path = "/lib.unstripped/lib" + _lib + shlib_extension
+      _lib_target = _target_prefix + _lib
 
       generate_resource_whitelist(_resource_whitelist_target) {
         _fat_lib_toolchain = ""
@@ -1390,7 +1360,7 @@
       }
     }
 
-    # This target is separate from monochrome_pak_assets because it does not
+    # This target is separate from monochrome_apk_pak_assets because it does not
     # disable compression.
     android_assets("${_variant}_locale_pak_assets") {
       renaming_sources = []
@@ -1430,43 +1400,20 @@
   # resource whitelist is derived from the native library.
   resource_packaging("chrome_apk_pak_assets") {
     is_monochrome = false
-    is_bundle = false
   }
   resource_packaging("monochrome_apk_pak_assets") {
     is_monochrome = true
-    is_bundle = false
   }
   resource_packaging("trichrome_chrome_apk_pak_assets") {
     is_monochrome = false
     is_trichrome = true
-    is_bundle = false
-  }
-  resource_packaging("chrome_bundle_pak_assets") {
-    is_monochrome = false
-    is_bundle = true
-  }
-  resource_packaging("monochrome_bundle_pak_assets") {
-    is_monochrome = true
-    is_bundle = true
-  }
-  resource_packaging("trichrome_chrome_bundle_pak_assets") {
-    is_monochrome = false
-    is_bundle = true
-    is_trichrome = true
-  }
-
-  # TODO(cjgrant): Remove this temporary alias after downstream renaming lands.
-  java_group("chrome_public_pak_assets") {
-    deps = [
-      ":chrome_apk_pak_assets",
-    ]
   }
 }  # current_toolchain == host_toolchain
 
 # Monochrome equivalent of Chrome's APK or bundle library template.
 template("libmonochrome_apk_or_bundle_tmpl") {
   chrome_common_shared_library(target_name) {
-    forward_variables_from(invoker, "*", [ "is_bundle" ])
+    forward_variables_from(invoker, "*")
     sources = [
       "../browser/android/monochrome_entry_point.cc",
     ]
@@ -1477,14 +1424,10 @@
 
     # Include appropriate factories for native feature modules if necessary.
     if (enable_vr) {
-      if (defined(invoker.is_bundle) && invoker.is_bundle) {
-        if (modularize_vr_native) {
-          deps += [ "//chrome/browser/android/vr:ui_module_factory" ]
-        } else {
-          deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ]
-        }
+      if (modularize_vr_native) {
+        deps += [ "//chrome/browser/android/vr:ui_module_factory" ]
       } else {
-        deps += [ "//chrome/browser/android/vr:ui_default_factory" ]
+        deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ]
       }
     }
 
@@ -1496,12 +1439,6 @@
     current_toolchain == android_secondary_abi_toolchain) {
   # Monochrome APK native library.
   libmonochrome_apk_or_bundle_tmpl("monochrome") {
-    is_bundle = false
-  }
-
-  # Monochrome bundle native library.
-  libmonochrome_apk_or_bundle_tmpl("monochrome_base") {
-    is_bundle = true
   }
 
   if (android_64bit_target_cpu) {
@@ -1510,19 +1447,10 @@
         ":monochrome_64($android_secondary_abi_toolchain)",
       ]
     }
-    group("monochrome_64_base_secondary_abi_lib") {
-      public_deps = [
-        ":monochrome_64_base($android_secondary_abi_toolchain)",
-      ]
-    }
   }
 } else {
   # 64-bit browser library targets (APK and bundle).
   libmonochrome_apk_or_bundle_tmpl("monochrome_64") {
-    is_bundle = false
-  }
-  libmonochrome_apk_or_bundle_tmpl("monochrome_64_base") {
-    is_bundle = true
   }
 
   # 32-bit browser library alias targets, pulled in by 64-bit WebView builds.
@@ -1531,11 +1459,6 @@
       ":monochrome($android_secondary_abi_toolchain)",
     ]
   }
-  group("monochrome_base_secondary_abi_lib") {
-    public_deps = [
-      ":monochrome_base($android_secondary_abi_toolchain)",
-    ]
-  }
 }
 
 # Java libraries that go into each public chrome APK and base module. The chrome
@@ -1663,7 +1586,7 @@
                              "enable_multidex",
                            ])
 
-    deps = _chrome_public_shared_deps
+    deps = _chrome_public_shared_deps + [ ":chrome_apk_pak_assets" ]
 
     if (_is_modern) {
       android_manifest = chrome_modern_public_android_manifest
@@ -1673,19 +1596,11 @@
       android_manifest_dep = ":chrome_public_android_manifest"
     }
 
-    if (invoker.target_type == "android_app_bundle_module") {
-      deps += [ ":chrome_${bundle_pak_asset_type}_pak_assets" ]
-      _suffix = bundle_library_suffix
-    } else {
-      deps += [ ":chrome_apk_pak_assets" ]
-      _suffix = ""
-    }
-
-    shared_libraries = [ ":libchrome${_suffix}" ]
+    shared_libraries = [ ":libchrome" ]
     add_unwind_tables_in_apk =
         _add_unwind_tables_in_chrome_32bit_apk && target_cpu == "arm"
     if (add_unwind_tables_in_apk) {
-      shared_library_for_unwind_asset = "chrome${_suffix}"
+      shared_library_for_unwind_asset = "chrome"
     }
 
     # Android supports webp transparent resources properly since API level 18,
@@ -1778,18 +1693,14 @@
       variables += trichrome_jinja_variables
     } else {
       _arch = ""
-      _lib_suffix = ""
       if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
         _arch = "_64"
       }
-      if (invoker.target_type == "android_app_bundle_module") {
-        _lib_suffix = bundle_library_suffix
-      }
       input = "java/AndroidManifest_monochrome.xml"
       includes += [ "//android_webview/apk/java/AndroidManifest.xml" ]
       variables += monochrome_android_manifest_jinja_variables + [
                      "target_sdk_version=$android_sdk_version",
-                     "webview_library=libmonochrome${_arch}${_lib_suffix}.so",
+                     "webview_library=libmonochrome${_arch}.so",
                      "include_arcore_manifest_flag=true",
                    ]
     }
@@ -2678,9 +2589,9 @@
     "java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java",
     "java/src/org/chromium/chrome/browser/ssl/SecurityStateModel.java",
     "java/src/org/chromium/chrome/browser/subresource_filter/TestSubresourceFilterPublisher.java",
-    "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java",
-    "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java",
     "java/src/org/chromium/chrome/browser/suggestions/SuggestionsEventReporterBridge.java",
+    "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java",
+    "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java",
     "java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java",
     "java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java",
     "java/src/org/chromium/chrome/browser/tab/Tab.java",
diff --git a/chrome/android/chrome_common_shared_library.gni b/chrome/android/chrome_common_shared_library.gni
index 65b1aae..9a8346d 100644
--- a/chrome/android/chrome_common_shared_library.gni
+++ b/chrome/android/chrome_common_shared_library.gni
@@ -10,13 +10,9 @@
 import("//chrome/android/features/dynamic_feature_modules.gni")
 import("//device/vr/buildflags/buildflags.gni")
 
-# These variables control whether app bundles use the same native libraries as
-# the APK, or bundle-specific versions. The bundle-specific versions may
-# diverge when code is moved from the base library into dynamic feature
-# modules. The variables can be removed when the bundle-specific lib
-# configuration has stabilized.
-bundle_library_suffix = "_base"
-bundle_pak_asset_type = "bundle"
+# TODO(cjgrant): Remove these variables once downstream stops using them.
+bundle_library_suffix = ""
+apk_pak_asset_type = "apk"
 
 # This value is set downstream for internal builds.
 if (!defined(default_chrome_orderfile)) {
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 45c1745b..4b7e2668 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1439,12 +1439,8 @@
   "java/src/org/chromium/chrome/browser/subresource_filter/TestSubresourceFilterPublisher.java",
   "java/src/org/chromium/chrome/browser/suggestions/DestructionObserver.java",
   "java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java",
-  "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java",
-  "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java",
   "java/src/org/chromium/chrome/browser/suggestions/NavigationRecorder.java",
   "java/src/org/chromium/chrome/browser/suggestions/OfflinableSuggestion.java",
-  "java/src/org/chromium/chrome/browser/suggestions/SiteSection.java",
-  "java/src/org/chromium/chrome/browser/suggestions/SiteSectionViewHolder.java",
   "java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java",
   "java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java",
   "java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java",
@@ -1457,16 +1453,20 @@
   "java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java",
   "java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java",
   "java/src/org/chromium/chrome/browser/suggestions/SuggestionsSheetVisibilityChangeObserver.java",
-  "java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java",
   "java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegate.java",
   "java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/suggestions/ThumbnailGradient.java",
-  "java/src/org/chromium/chrome/browser/suggestions/Tile.java",
-  "java/src/org/chromium/chrome/browser/suggestions/TileGridLayout.java",
-  "java/src/org/chromium/chrome/browser/suggestions/TileGridViewHolder.java",
-  "java/src/org/chromium/chrome/browser/suggestions/TileGroup.java",
-  "java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java",
-  "java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java",
+  "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java",
+  "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/SiteSectionViewHolder.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayout.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TileGridViewHolder.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java",
   "java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java",
   "java/src/org/chromium/chrome/browser/survey/SurveyController.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 3668ba2..1c5f0d5f 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -175,8 +175,7 @@
   "junit/src/org/chromium/chrome/browser/signin/SigninPromoUtilTest.java",
   "junit/src/org/chromium/chrome/browser/snackbar/SnackbarCollectionUnitTest.java",
   "junit/src/org/chromium/chrome/browser/suggestions/SuggestionsImageFetcherTest.java",
-  "junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java",
-  "junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java",
+  "junit/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java",
   "junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java",
   "junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java",
   "junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 07a858e2..7a268f3b 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -304,31 +304,26 @@
     ]
 
     if (is_monochrome) {
-      if (invoker.target_type == "android_app_bundle_module") {
-        _suffix = bundle_library_suffix
-      } else {
-        _suffix = ""
-      }
       if (android_64bit_target_cpu) {
         # Build //android_webview:monochrome with the opposite bitness that
         # Chrome runs in.
         if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
-          shared_libraries = [ "//chrome/android:monochrome_64${_suffix}" ]
+          shared_libraries = [ "//chrome/android:monochrome_64" ]
           if (build_apk_secondary_abi && invoker.include_32_bit_webview) {
-            secondary_abi_shared_libraries = [ "//android_webview:monochrome_64${_suffix}($android_secondary_abi_toolchain)" ]
+            secondary_abi_shared_libraries = [ "//android_webview:monochrome_64($android_secondary_abi_toolchain)" ]
           }
         } else {
-          shared_libraries = [ "//android_webview:monochrome${_suffix}" ]
+          shared_libraries = [ "//android_webview:monochrome" ]
           if (build_apk_secondary_abi) {
             secondary_abi_shared_libraries =
-                [ "//chrome/android:monochrome${_suffix}_secondary_abi_lib" ]
+                [ "//chrome/android:monochrome_secondary_abi_lib" ]
           }
         }
       } else {
-        shared_libraries = [ "//chrome/android:monochrome${_suffix}" ]
+        shared_libraries = [ "//chrome/android:monochrome" ]
       }
       if (invoker.add_unwind_tables_in_apk) {
-        shared_library_for_unwind_asset = "monochrome${_suffix}"
+        shared_library_for_unwind_asset = "monochrome"
       }
 
       _deps += [
@@ -403,13 +398,7 @@
     # Android N+ better supports multiple locales (https://crbug.com/780847).
     support_zh_hk = false
 
-    if (invoker.target_type == "android_app_bundle_module") {
-      _deps += [
-        "//chrome/android:${_pak_prefix}_${bundle_pak_asset_type}_pak_assets",
-      ]
-    } else {
-      _deps += [ "//chrome/android:${_pak_prefix}_apk_pak_assets" ]
-    }
+    _deps += [ "//chrome/android:${_pak_prefix}_apk_pak_assets" ]
 
     if (_enable_multidex && invoker.target_type == "android_apk" &&
         !defined(invoker.negative_main_dex_globs)) {
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index af81503..bca0154 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -420,8 +420,8 @@
   "javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetTestRule.java",
   "javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetUiCaptureTest.java",
   "javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsSheetVisibilityChangeObserverTest.java",
-  "javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java",
-  "javatests/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java",
+  "javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java",
+  "javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java",
   "javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
index 21ee7c3..b54c308 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -21,7 +21,6 @@
 import org.chromium.chrome.browser.tabmodel.TabList;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -43,10 +42,10 @@
     private final TabGridDialogCoordinator mTabGridDialogCoordinator;
 
     public GridTabSwitcherCoordinator(Context context,
-            ActivityLifecycleDispatcher lifecycleDispatcher, ToolbarManager toolbarManager,
-            TabModelSelector tabModelSelector, TabContentManager tabContentManager,
-            CompositorViewHolder compositorViewHolder, ChromeFullscreenManager fullscreenManager,
-            TabCreatorManager tabCreatorManager, Runnable backPress) {
+            ActivityLifecycleDispatcher lifecycleDispatcher, TabModelSelector tabModelSelector,
+            TabContentManager tabContentManager, CompositorViewHolder compositorViewHolder,
+            ChromeFullscreenManager fullscreenManager, TabCreatorManager tabCreatorManager,
+            Runnable backPress) {
         PropertyModel containerViewModel = new PropertyModel(TabListContainerProperties.ALL_KEYS);
         TabListMediator.GridCardOnClickListenerProvider gridCardOnClickListenerProvider;
         if (FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
index d3d3b0c..3d66b85b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -27,9 +27,9 @@
                     "Downloaded_Enabled");
         }
         return new GridTabSwitcherCoordinator(activity, activity.getLifecycleDispatcher(),
-                activity.getToolbarManager(), activity.getTabModelSelector(),
-                activity.getTabContentManager(), activity.getCompositorViewHolder(),
-                activity.getFullscreenManager(), activity, activity::onBackPressed);
+                activity.getTabModelSelector(), activity.getTabContentManager(),
+                activity.getCompositorViewHolder(), activity.getFullscreenManager(), activity,
+                activity::onBackPressed);
     }
 
     @Override
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
index 1d46ded..d4d87ca 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
@@ -79,7 +79,6 @@
     private static final String TAB1_TITLE = "Tab1";
     private static final String TAB2_TITLE = "Tab2";
     private static final String TAB3_TITLE = "Tab3";
-    private static final String NEW_TITLE = "New title";
     private static final int TAB1_ID = 456;
     private static final int TAB2_ID = 789;
     private static final int TAB3_ID = 123;
@@ -115,8 +114,6 @@
     ArgumentCaptor<TabModelSelectorObserver> mTabModelSelectorObserverCaptor;
     @Captor
     ArgumentCaptor<ChromeFullscreenManager.FullscreenListener> mFullscreenListenerCaptor;
-    @Captor
-    ArgumentCaptor<Tab> mTabCaptor;
 
     private Tab mTab1;
     private Tab mTab2;
diff --git a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
index e5fe8a4c..64d14ff 100644
--- a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
+++ b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
@@ -1161,9 +1161,7 @@
     <meta-data
         android:name="android.content.APP_RESTRICTIONS"
         android:resource="@xml/app_restrictions"/>
-    <meta-data
-        android:name="com.android.webview.WebViewLibrary"
-        android:value="libmonochrome_base.so"/>
+    <meta-data android:name="com.android.webview.WebViewLibrary" android:value="libmonochrome.so"/>
     <meta-data
         android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
         android:value="org.chromium.chrome.browser.media.router.caf.CastOptionsProvider"/>
diff --git a/chrome/android/java/res/drawable/tile_view_hairline_border_background.xml b/chrome/android/java/res/drawable/tile_view_hairline_border_background.xml
new file mode 100644
index 0000000..ba8478d
--- /dev/null
+++ b/chrome/android/java/res/drawable/tile_view_hairline_border_background.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 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. -->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="@color/modern_primary_color" />
+    <stroke android:width="1dp" android:color="@color/hairline_stroke_color"/>
+</shape>
diff --git a/chrome/android/java/res/layout/explore_sites_category_card_view.xml b/chrome/android/java/res/layout/explore_sites_category_card_view.xml
index 011925b..05eae1d 100644
--- a/chrome/android/java/res/layout/explore_sites_category_card_view.xml
+++ b/chrome/android/java/res/layout/explore_sites_category_card_view.xml
@@ -24,7 +24,7 @@
         android:minHeight="@dimen/explore_sites_category_title_height"
         tools:text="Category" />
 
-    <org.chromium.chrome.browser.suggestions.TileGridLayout
+    <org.chromium.chrome.browser.suggestions.tile.TileGridLayout
         android:id="@+id/category_sites"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/layout/explore_sites_section.xml b/chrome/android/java/res/layout/explore_sites_section.xml
index f5f341e..fbed051 100644
--- a/chrome/android/java/res/layout/explore_sites_section.xml
+++ b/chrome/android/java/res/layout/explore_sites_section.xml
@@ -3,7 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<org.chromium.chrome.browser.suggestions.TileGridLayout
+<org.chromium.chrome.browser.suggestions.tile.TileGridLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/layout/suggestions_site_tile_grid_modern.xml b/chrome/android/java/res/layout/suggestions_site_tile_grid_modern.xml
index 11364d1..acc933ca 100644
--- a/chrome/android/java/res/layout/suggestions_site_tile_grid_modern.xml
+++ b/chrome/android/java/res/layout/suggestions_site_tile_grid_modern.xml
@@ -3,7 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<org.chromium.chrome.browser.suggestions.TileGridLayout
+<org.chromium.chrome.browser.suggestions.tile.TileGridLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/tile_grid_layout"
     android:layout_width="match_parent"
diff --git a/chrome/android/java/res/layout/suggestions_tile_view.xml b/chrome/android/java/res/layout/suggestions_tile_view.xml
index ba00700..971c8be 100644
--- a/chrome/android/java/res/layout/suggestions_tile_view.xml
+++ b/chrome/android/java/res/layout/suggestions_tile_view.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file. -->
 
 <!-- A site suggestion tile. -->
-<org.chromium.chrome.browser.suggestions.SuggestionsTileView
+<org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -16,4 +16,4 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         layout="@layout/tile_view_modern" />
-</org.chromium.chrome.browser.suggestions.SuggestionsTileView>
+</org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView>
diff --git a/chrome/android/java/res/layout/suggestions_tile_view_condensed.xml b/chrome/android/java/res/layout/suggestions_tile_view_condensed.xml
index 820b0c5..eebce13 100644
--- a/chrome/android/java/res/layout/suggestions_tile_view_condensed.xml
+++ b/chrome/android/java/res/layout/suggestions_tile_view_condensed.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file. -->
 
 <!-- A site suggestion tile. -->
-<org.chromium.chrome.browser.suggestions.SuggestionsTileView
+<org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -16,4 +16,4 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         layout="@layout/tile_view_modern_condensed" />
-</org.chromium.chrome.browser.suggestions.SuggestionsTileView>
+</org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView>
diff --git a/chrome/android/java/res/layout/top_sites_tile_view.xml b/chrome/android/java/res/layout/top_sites_tile_view.xml
index aca78aa7..1b683063 100644
--- a/chrome/android/java/res/layout/top_sites_tile_view.xml
+++ b/chrome/android/java/res/layout/top_sites_tile_view.xml
@@ -7,7 +7,6 @@
 <org.chromium.chrome.browser.suggestions.tile.TopSitesTileView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="@dimen/tile_view_width"
     android:layout_height="wrap_content"
     android:paddingStart="@dimen/tile_view_padding"
@@ -20,7 +19,7 @@
         android:layout_height="@dimen/tile_view_icon_size"
         android:layout_gravity="center_horizontal"
         android:layout_marginTop="@dimen/tile_view_icon_background_margin_top_modern"
-        android:background="@drawable/tile_view_icon_background_modern" />
+        android:background="@drawable/tile_view_hairline_border_background" />
 
     <!-- The main icon. -->
     <ImageView
diff --git a/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml b/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml
index 1ca4fe9..ff97c3e 100644
--- a/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml
+++ b/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml
@@ -7,7 +7,6 @@
 <org.chromium.chrome.browser.suggestions.tile.TopSitesTileView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="@dimen/tile_view_width_condensed"
     android:layout_height="wrap_content"
     android:paddingStart="@dimen/tile_view_padding"
@@ -20,7 +19,7 @@
         android:layout_height="@dimen/tile_view_icon_size"
         android:layout_gravity="center_horizontal"
         android:layout_marginTop="@dimen/tile_view_icon_background_margin_top_modern"
-        android:background="@drawable/tile_view_icon_background_modern" />
+        android:background="@drawable/tile_view_hairline_border_background" />
 
     <!-- The main icon. -->
     <ImageView
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 13c4232..6892ee4 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -313,7 +313,6 @@
     <!-- NTP dimensions -->
     <dimen name="tile_grid_layout_max_width">504dp</dimen>
     <dimen name="tile_grid_layout_padding_top">24dp</dimen>
-    <dimen name="tile_grid_layout_padding_start">12dp</dimen>
     <dimen name="tile_grid_layout_no_logo_padding_top">20dp</dimen>
     <dimen name="tile_grid_layout_bleed">8dp</dimen>
     <dimen name="tile_grid_layout_vertical_spacing">6dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
index 819d6108..f1c72253 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
@@ -39,6 +39,7 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.blink_public.platform.WebDisplayMode;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.webapps.WebApkInfo;
 import org.chromium.chrome.browser.webapps.WebappActivity;
 import org.chromium.chrome.browser.webapps.WebappAuthenticator;
@@ -274,7 +275,12 @@
      */
     private static void showAddedToHomescreenToast(final String title) {
         Context applicationContext = ContextUtils.getApplicationContext();
-        String toastText = applicationContext.getString(R.string.added_to_homescreen, title);
+        String toastText;
+        if (FeatureUtilities.isNoTouchModeEnabled()) {
+            toastText = applicationContext.getString(R.string.added_to_apps, title);
+        } else {
+            toastText = applicationContext.getString(R.string.added_to_homescreen, title);
+        }
         showToast(toastText);
     }
 
@@ -701,7 +707,8 @@
 
     private static boolean isRequestPinShortcutSupported() {
         if (!sCheckedIfRequestPinShortcutSupported) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+                    && !FeatureUtilities.isNoTouchModeEnabled()) {
                 checkIfRequestPinShortcutSupported();
             }
             sCheckedIfRequestPinShortcutSupported = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
index 0eec83e4..51f956f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
@@ -20,7 +20,7 @@
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.suggestions.TileGridLayout;
+import org.chromium.chrome.browser.suggestions.tile.TileGridLayout;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.base.PageTransition;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
index d29deb4..cd9155f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
@@ -23,7 +23,7 @@
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle;
-import org.chromium.chrome.browser.suggestions.TileGridLayout;
+import org.chromium.chrome.browser.suggestions.tile.TileGridLayout;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.content_public.browser.LoadUrlParams;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
index 239af73..1d16e4e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
@@ -178,6 +178,8 @@
         String template = getContext().getString(R.string.duplicate_download_request_infobar_text);
 
         model.set(ModalDialogProperties.TITLE,
+                getContext().getResources().getString(R.string.menu_download));
+        model.set(ModalDialogProperties.MESSAGE,
                 getDownloadMessageText(getContext(), template).toString());
 
         return model;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 7c25482..9ec9648 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -47,9 +47,9 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsMetrics;
 import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
-import org.chromium.chrome.browser.suggestions.Tile;
-import org.chromium.chrome.browser.suggestions.TileGroup;
-import org.chromium.chrome.browser.suggestions.TileGroupDelegateImpl;
+import org.chromium.chrome.browser.suggestions.tile.Tile;
+import org.chromium.chrome.browser.suggestions.tile.TileGroup;
+import org.chromium.chrome.browser.suggestions.tile.TileGroupDelegateImpl;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.Tab.TabHidingType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
index 25f0a8f..a854524 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -41,15 +41,15 @@
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.suggestions.SiteSection;
-import org.chromium.chrome.browser.suggestions.SiteSectionViewHolder;
 import org.chromium.chrome.browser.suggestions.SiteSuggestion;
 import org.chromium.chrome.browser.suggestions.SuggestionsConfig;
 import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory;
-import org.chromium.chrome.browser.suggestions.Tile;
-import org.chromium.chrome.browser.suggestions.TileGridLayout;
-import org.chromium.chrome.browser.suggestions.TileGroup;
-import org.chromium.chrome.browser.suggestions.TileRenderer;
+import org.chromium.chrome.browser.suggestions.tile.SiteSection;
+import org.chromium.chrome.browser.suggestions.tile.SiteSectionViewHolder;
+import org.chromium.chrome.browser.suggestions.tile.Tile;
+import org.chromium.chrome.browser.suggestions.tile.TileGridLayout;
+import org.chromium.chrome.browser.suggestions.tile.TileGroup;
+import org.chromium.chrome.browser.suggestions.tile.TileRenderer;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index f258c4b..ed6823c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -27,7 +27,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
-import org.chromium.chrome.browser.suggestions.TileGroup;
+import org.chromium.chrome.browser.suggestions.tile.TileGroup;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index e294900..3c8efd2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -73,8 +73,6 @@
 public class LocationBarLayout extends FrameLayout
         implements OnClickListener, LocationBar, AutocompleteDelegate, FakeboxDelegate,
                    LocationBarVoiceRecognitionHandler.Delegate {
-    private static final String TAG = "cr_LocationBar";
-
     protected ImageButton mDeleteButton;
     protected ImageButton mMicButton;
     protected View mUrlBar;
@@ -106,7 +104,6 @@
 
     private OmniboxPrerender mOmniboxPrerender;
 
-    private boolean mOmniboxVoiceSearchAlwaysVisible;
     protected float mUrlFocusChangePercent;
     protected LinearLayout mUrlActionContainer;
 
@@ -403,9 +400,7 @@
         mUrlFocusChangeInProgress = inProgress;
         if (!inProgress) {
             updateButtonVisibility();
-        }
 
-        if (!inProgress) {
             // The accessibility bounding box is not properly updated when focusing the Omnibox
             // from the NTP fakebox.  Clearing/re-requesting focus triggers the bounding box to
             // be recalculated.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
index 6d9e539a..27c98a07 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -311,9 +311,7 @@
     public void addAcceptedPaymentMethodIfRecognized(PaymentMethodData data) {
         assert data != null;
         String method = data.supportedMethod;
-        if (mCardIssuerNetworks.containsKey(method)) {
-            addAcceptedNetwork(method);
-        } else if (BasicCardUtils.BASIC_CARD_METHOD_NAME.equals(method)) {
+        if (BasicCardUtils.BASIC_CARD_METHOD_NAME.equals(method)) {
             Set<String> basicCardNetworks = BasicCardUtils.convertBasicCardToNetworks(data);
             mAcceptedBasicCardIssuerNetworks.addAll(basicCardNetworks);
             for (String network : basicCardNetworks) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java
deleted file mode 100644
index 566c7c4..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.support.annotation.Nullable;
-
-import org.chromium.base.annotations.CalledByNative;
-
-import java.util.List;
-
-/**
- * Methods to provide most recent urls, titles and thumbnails.
- */
-public interface MostVisitedSites {
-    /**
-     * An interface for handling events in {@link MostVisitedSites}.
-     */
-    interface Observer {
-        /** This is called when the list of most visited URLs is initially available or updated. */
-        void onSiteSuggestionsAvailable(List<SiteSuggestion> siteSuggestions);
-
-        /**
-         * This is called when a previously uncached icon has been fetched.
-         * Parameters guaranteed to be non-null.
-         *
-         * @param siteUrl URL of site with newly-cached icon.
-         */
-        void onIconMadeAvailable(String siteUrl);
-    }
-
-    /**
-     * An interface to provide {@link MostVisitedSites} with platform-specific home page data.
-     */
-    interface HomepageClient {
-        /**
-         * @return True if homepage tile should be shown.
-         */
-        @CalledByNative("HomepageClient")
-        boolean isHomepageTileEnabled();
-
-        /**
-         * @return The raw URL of the currently set home page.
-         */
-        @CalledByNative("HomepageClient")
-        @Nullable
-        String getHomepageUrl();
-    }
-
-    /**
-     * This instance must not be used after calling destroy().
-     */
-    void destroy();
-
-    /**
-     * Sets the recipient for events from {@link MostVisitedSites}. The observer may be notified
-     * synchronously or asynchronously.
-     * @param observer The observer to be notified.
-     * @param numSites The maximum number of sites to return.
-     */
-    void setObserver(Observer observer, int numSites);
-
-    /**
-     * Blacklists a URL from the most visited URLs list.
-     */
-    void addBlacklistedUrl(String url);
-
-    /**
-     * Removes a URL from the most visited URLs blacklist.
-     */
-    void removeBlacklistedUrl(String url);
-
-    /**
-     * Records metrics about an impression of the surface with tiles.
-     * @param tilesCount Count of tiles available on the surface at the moment.
-     */
-    void recordPageImpression(int tilesCount);
-
-    /**
-     * Records metrics about an impression of a tile including its source (local, server, ...) and
-     * its visual type.
-     * @param tile Object holding the details of a tile.
-     */
-    void recordTileImpression(Tile tile);
-
-    /**
-     * Records the opening of a Most Visited Item.
-     * @param tile Object holding the details of a tile.
-     */
-    void recordOpenedMostVisitedItem(Tile tile);
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java
deleted file mode 100644
index b30a92a9..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java
+++ /dev/null
@@ -1,195 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.text.TextUtils;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNIAdditionalImport;
-import org.chromium.chrome.browser.ntp.NewTabPage;
-import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.util.FeatureUtilities;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Methods to bridge into native history to provide most recent urls, titles and thumbnails.
- */
-@JNIAdditionalImport(MostVisitedSites.class) // Needed for the Observer usage in the native calls.
-public class MostVisitedSitesBridge
-        implements MostVisitedSites, HomepageManager.HomepageStateListener {
-    /**
-     * Maximum number of tiles that is explicitly supported. UMA relies on this value, so even if
-     * the UI supports it, getting more can raise unexpected issues.
-     */
-    public static final int MAX_TILE_COUNT = 12;
-
-    private long mNativeMostVisitedSitesBridge;
-
-    private MostVisitedSites.Observer mWrappedObserver;
-
-    /**
-     * MostVisitedSites constructor requires a valid user profile object.
-     *
-     * @param profile The profile for which to fetch most visited sites.
-     */
-    public MostVisitedSitesBridge(Profile profile) {
-        mNativeMostVisitedSitesBridge = nativeInit(profile);
-        // The first tile replaces is replaced with homepage tile if NTPButton is enabled. Setting
-        // a homepage client to provide Java side information.
-        if (FeatureUtilities.isNewTabPageButtonEnabled()
-                && FeatureUtilities.isHomepageTileEnabled()) {
-            nativeSetHomepageClient(mNativeMostVisitedSitesBridge, new HomepageClient() {
-                @Override
-                public boolean isHomepageTileEnabled() {
-                    return HomepageManager.isHomepageEnabled()
-                            && !NewTabPage.isNTPUrl(getHomepageUrl())
-                            && !TextUtils.isEmpty(HomepageManager.getHomepageUri());
-                }
-
-                @Override
-                public String getHomepageUrl() {
-                    return HomepageManager.getHomepageUri();
-                }
-            });
-            HomepageManager.getInstance().addListener(this);
-        }
-    }
-
-    /**
-     * Cleans up the C++ side of this class. This instance must not be used after calling destroy().
-     */
-    @Override
-    public void destroy() {
-        // Stop listening even if it was not started in the first place. (Handled without errors.)
-        HomepageManager.getInstance().removeListener(this);
-        assert mNativeMostVisitedSitesBridge != 0;
-        nativeDestroy(mNativeMostVisitedSitesBridge);
-        mNativeMostVisitedSitesBridge = 0;
-    }
-
-    @Override
-    public void setObserver(Observer observer, int numSites) {
-        assert numSites <= MAX_TILE_COUNT;
-        mWrappedObserver = observer;
-
-        nativeSetObserver(mNativeMostVisitedSitesBridge, this, numSites);
-    }
-
-    @Override
-    public void addBlacklistedUrl(String url) {
-        nativeAddOrRemoveBlacklistedUrl(mNativeMostVisitedSitesBridge, url, true);
-    }
-
-    @Override
-    public void removeBlacklistedUrl(String url) {
-        nativeAddOrRemoveBlacklistedUrl(mNativeMostVisitedSitesBridge, url, false);
-    }
-
-    @Override
-    public void recordPageImpression(int tilesCount) {
-        nativeRecordPageImpression(mNativeMostVisitedSitesBridge, tilesCount);
-    }
-
-    @Override
-    public void recordTileImpression(Tile tile) {
-        nativeRecordTileImpression(mNativeMostVisitedSitesBridge, tile.getIndex(), tile.getType(),
-                tile.getIconType(), tile.getTitleSource(), tile.getSource(),
-                tile.getData().dataGenerationTime.getTime(), tile.getUrl());
-    }
-
-    @Override
-    public void recordOpenedMostVisitedItem(Tile tile) {
-        nativeRecordOpenedMostVisitedItem(mNativeMostVisitedSitesBridge, tile.getIndex(),
-                tile.getType(), tile.getTitleSource(), tile.getSource(),
-                tile.getData().dataGenerationTime.getTime());
-    }
-
-    @Override
-    public void onHomepageStateUpdated() {
-        assert mNativeMostVisitedSitesBridge != 0;
-        // Ensure even a blacklisted homepage can be set as tile when (re-)enabling it.
-        if (HomepageManager.isHomepageEnabled()) {
-            removeBlacklistedUrl(HomepageManager.getHomepageUri());
-        }
-        nativeOnHomepageStateChanged(mNativeMostVisitedSitesBridge);
-    }
-
-    /**
-     * Utility function to convert JNI friendly site suggestion data to a Java friendly list of
-     * {@link SiteSuggestion}s.
-     */
-    public static List<SiteSuggestion> buildSiteSuggestions(String[] titles, String[] urls,
-            int[] sections, String[] whitelistIconPaths, int[] titleSources, int[] sources,
-            long[] dataGenerationTimesMs) {
-        List<SiteSuggestion> siteSuggestions = new ArrayList<>(titles.length);
-        for (int i = 0; i < titles.length; ++i) {
-            siteSuggestions.add(new SiteSuggestion(titles[i], urls[i], whitelistIconPaths[i],
-                    titleSources[i], sources[i], sections[i], new Date(dataGenerationTimesMs[i])));
-        }
-        return siteSuggestions;
-    }
-
-    /**
-     * This is called when the list of most visited URLs is initially available or updated.
-     * Parameters guaranteed to be non-null.
-     *
-     * @param titles Array of most visited url page titles.
-     * @param urls Array of most visited URLs, including popular URLs if
-     *             available and necessary (i.e. there aren't enough most
-     *             visited URLs).
-     * @param whitelistIconPaths The paths to the icon image files for whitelisted tiles, empty
-     *                           strings otherwise.
-     * @param sources For each tile, the {@code TileSource} that generated the tile.
-     */
-    @CalledByNative
-    private void onURLsAvailable(String[] titles, String[] urls, int[] sections,
-            String[] whitelistIconPaths, int[] titleSources, int[] sources,
-            long[] dataGenerationTimesMs) {
-        // Don't notify observer if we've already been destroyed.
-        if (mNativeMostVisitedSitesBridge == 0) return;
-
-        List<SiteSuggestion> suggestions = new ArrayList<>();
-
-        suggestions.addAll(buildSiteSuggestions(titles, urls, sections, whitelistIconPaths,
-                titleSources, sources, dataGenerationTimesMs));
-
-        mWrappedObserver.onSiteSuggestionsAvailable(suggestions);
-    }
-
-    /**
-     * This is called when a previously uncached icon has been fetched.
-     * Parameters guaranteed to be non-null.
-     *
-     * @param siteUrl URL of site with newly-cached icon.
-     */
-    @CalledByNative
-    private void onIconMadeAvailable(String siteUrl) {
-        // Don't notify observer if we've already been destroyed.
-        if (mNativeMostVisitedSitesBridge != 0) {
-            mWrappedObserver.onIconMadeAvailable(siteUrl);
-        }
-    }
-
-    private native long nativeInit(Profile profile);
-    private native void nativeDestroy(long nativeMostVisitedSitesBridge);
-    private native void nativeOnHomepageStateChanged(long nativeMostVisitedSitesBridge);
-    private native void nativeSetHomepageClient(
-            long nativeMostVisitedSitesBridge, MostVisitedSites.HomepageClient homePageClient);
-    private native void nativeSetObserver(
-            long nativeMostVisitedSitesBridge, MostVisitedSitesBridge observer, int numSites);
-    private native void nativeAddOrRemoveBlacklistedUrl(
-            long nativeMostVisitedSitesBridge, String url, boolean addUrl);
-    private native void nativeRecordPageImpression(
-            long nativeMostVisitedSitesBridge, int tilesCount);
-    private native void nativeRecordTileImpression(long nativeMostVisitedSitesBridge, int index,
-            int type, int iconType, int titleSource, int source, long dataGenerationTimeMs,
-            String url);
-    private native void nativeRecordOpenedMostVisitedItem(long nativeMostVisitedSitesBridge,
-            int index, int tileType, int titleSource, int source, long dataGenerationTimeMs);
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSection.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSection.java
deleted file mode 100644
index bc5b66e..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSection.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.support.annotation.LayoutRes;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
-import org.chromium.chrome.browser.native_page.ContextMenuManager;
-import org.chromium.chrome.browser.ntp.cards.ItemViewType;
-import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
-import org.chromium.chrome.browser.ntp.cards.OptionalLeaf;
-import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-
-/**
- * The model and controller for a group of site suggestions.
- * @deprecated This class is still being used, but not in the New Tab Page RecyclerView
- *         anymore. It still uses the latter's base classes until SiteSection is migrated to the new
- *         UI architecture.
- */
-@Deprecated
-public class SiteSection extends OptionalLeaf implements TileGroup.Observer {
-    /**
-     * The maximum number of tiles to try and fit in a row. On smaller screens, there may not be
-     * enough space to fit all of them.
-     */
-    private static final int MAX_TILE_COLUMNS = 4;
-    private static final int TILE_TITLE_LINES = 1;
-
-    private final TileGroup mTileGroup;
-    private final TileRenderer mTileRenderer;
-
-    public static ViewGroup inflateSiteSection(ViewGroup parent) {
-        return (ViewGroup) LayoutInflater.from(parent.getContext())
-                .inflate(getLayout(), parent, false);
-    }
-
-    public static SiteSectionViewHolder createViewHolder(ViewGroup view, UiConfig uiConfig) {
-        return new TileGridViewHolder(view, getMaxTileRows(), MAX_TILE_COLUMNS, uiConfig);
-    }
-
-    public SiteSection(SuggestionsUiDelegate uiDelegate, ContextMenuManager contextMenuManager,
-            TileGroup.Delegate tileGroupDelegate, OfflinePageBridge offlinePageBridge,
-            UiConfig uiConfig) {
-        mTileRenderer = new TileRenderer(ContextUtils.getApplicationContext(),
-                SuggestionsConfig.getTileStyle(uiConfig), TILE_TITLE_LINES,
-                uiDelegate.getImageFetcher());
-        mTileGroup = new TileGroup(mTileRenderer, uiDelegate, contextMenuManager, tileGroupDelegate,
-                /* observer = */ this, offlinePageBridge);
-        mTileGroup.startObserving(MAX_TILE_COLUMNS * getMaxTileRows());
-    }
-
-    @Override
-    @ItemViewType
-    protected int getItemViewType() {
-        // Throw an exception instead of just `assert false` to avoid compiler warnings about the
-        // return value.
-        throw new IllegalStateException();
-    }
-
-    @Override
-    protected void onBindViewHolder(NewTabPageViewHolder holder) {
-        SiteSectionViewHolder siteSectionView = (SiteSectionViewHolder) holder;
-        siteSectionView.bindDataSource(mTileGroup, mTileRenderer);
-        siteSectionView.refreshData();
-    }
-
-    @Override
-    public String describeForTesting() {
-        // Throw an exception instead of just `assert false` to avoid compiler warnings about the
-        // return value.
-        throw new IllegalStateException();
-    }
-
-    @Override
-    public void onTileDataChanged() {
-        setVisibilityInternal(!mTileGroup.isEmpty());
-        if (!isVisible()) return;
-        notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).refreshData());
-    }
-
-    @Override
-    public void onTileCountChanged() {
-        onTileDataChanged();
-    }
-
-    @Override
-    public void onTileIconChanged(Tile tile) {
-        if (!isVisible()) return;
-        notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).updateIconView(tile));
-    }
-
-    @Override
-    public void onTileOfflineBadgeVisibilityChanged(Tile tile) {
-        if (!isVisible()) return;
-        notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).updateOfflineBadge(tile));
-    }
-
-    TileGroup getTileGroupForTesting() {
-        return mTileGroup;
-    }
-
-    private static int getMaxTileRows() {
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.EXPLORE_SITES)
-                && !ExploreSitesBridge.isIntegratedWithMostLikely(
-                        ExploreSitesBridge.getVariation())) {
-            return 1;
-        }
-        return 2;
-    }
-
-    @LayoutRes
-    private static int getLayout() {
-        return R.layout.suggestions_site_tile_grid_modern;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSectionViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSectionViewHolder.java
deleted file mode 100644
index 25b585a..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSectionViewHolder.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.support.annotation.CallSuper;
-import android.support.annotation.Nullable;
-import android.view.View;
-
-import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
-
-/**
- * Describes a portion of UI responsible for rendering a group of sites. It abstracts general tasks
- * related to initialising and updating this UI.
- */
-public abstract class SiteSectionViewHolder extends NewTabPageViewHolder {
-    protected TileGroup mTileGroup;
-    protected TileRenderer mTileRenderer;
-
-    /**
-     * Constructs a {@link SiteSectionViewHolder} used to display tiles in both NTP and Chrome Home.
-     *
-     * @param itemView The {@link View} for this item
-     */
-    public SiteSectionViewHolder(View itemView) {
-        super(itemView);
-    }
-
-    /** Initialise the view, letting it know the data it will have to display. */
-    @CallSuper
-    public void bindDataSource(TileGroup tileGroup, TileRenderer tileRenderer) {
-        mTileGroup = tileGroup;
-        mTileRenderer = tileRenderer;
-    }
-
-    /**
-     * Sets a new icon on the child view with a matching site.
-     * @param tile The tile that holds the data to populate the tile view.
-     */
-    public void updateIconView(Tile tile) {
-        SuggestionsTileView tileView = findTileView(tile.getData());
-        if (tileView != null) tileView.renderIcon(tile);
-    }
-
-    /**
-     * Updates the visibility of the offline badge on the child view with a matching site.
-     * @param tile The tile that holds the data to populate the tile view.
-     */
-    public void updateOfflineBadge(Tile tile) {
-        SuggestionsTileView tileView = findTileView(tile.getData());
-        if (tileView != null) tileView.renderOfflineBadge(tile);
-    }
-
-    /** Clears the current data and displays the current state of the model. */
-    public abstract void refreshData();
-
-    @Nullable
-    public abstract SuggestionsTileView findTileView(SiteSuggestion data);
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java
index d16a5307..1826df1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java
@@ -4,6 +4,10 @@
 
 package org.chromium.chrome.browser.suggestions;
 
+import org.chromium.chrome.browser.suggestions.tile.TileSectionType;
+import org.chromium.chrome.browser.suggestions.tile.TileSource;
+import org.chromium.chrome.browser.suggestions.tile.TileTitleSource;
+
 import java.util.Date;
 
 /**
@@ -27,7 +31,10 @@
     @TileSource
     public final int source;
 
-    /** The {@link TileSectionType} the tile is contained in. */
+    /**
+     * The {@link org.chromium.chrome.browser.suggestions.tile.TileSectionType} the tile is
+     * contained in.
+     */
     @TileSectionType
     public final int sectionType;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
index 1aec6f0..20414d5c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
@@ -17,6 +17,8 @@
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSitesBridge;
 import org.chromium.chrome.browser.widget.ThumbnailProvider;
 import org.chromium.chrome.browser.widget.ThumbnailProviderImpl;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
index 78b7274..fd06fcf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSitesBridge;
 import org.chromium.chrome.browser.tab.Tab;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java
deleted file mode 100644
index b20a7d91..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.AttributeSet;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ntp.TitleUtil;
-import org.chromium.chrome.browser.widget.tile.TileWithTextView;
-
-/**
- * The view for a site suggestion tile. Displays the title of the site beneath a large icon. If a
- * large icon isn't available, displays a rounded rectangle with a single letter in its place.
- */
-public class SuggestionsTileView extends TileWithTextView {
-    /** The data currently associated to this tile. */
-    private SiteSuggestion mData;
-
-    /**
-     * Constructor for inflating from XML.
-     */
-    public SuggestionsTileView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    /**
-     * Initializes the view using the data held by {@code tile}. This should be called immediately
-     * after inflation.
-     * @param tile The tile that holds the data to populate this view.
-     * @param titleLines The number of text lines to use for the tile title.
-     */
-    public void initialize(Tile tile, int titleLines) {
-        super.initialize(TitleUtil.getTitleForDisplay(tile.getTitle(), tile.getUrl()),
-                tile.isOfflineAvailable(), tile.getIcon(), titleLines);
-        mData = tile.getData();
-        setIconViewLayoutParams(tile);
-    }
-
-    /** Retrieves data associated with this view.  */
-    public SiteSuggestion getData() {
-        return mData;
-    }
-
-    /** Retrieves url associated with this view. */
-    public String getUrl() {
-        return mData.url;
-    }
-
-    /** Renders icon based on tile data.  */
-    public void renderIcon(Tile tile) {
-        setIconDrawable(tile.getIcon());
-        setIconViewLayoutParams(tile);
-    }
-
-    public void renderOfflineBadge(Tile tile) {
-        setOfflineBadgeVisibility(tile.isOfflineAvailable());
-    }
-
-    protected void setIconViewLayoutParams(Tile tile) {
-        MarginLayoutParams params = (MarginLayoutParams) mIconView.getLayoutParams();
-        Resources resources = getResources();
-        if (tile.getType() == TileVisualType.ICON_COLOR
-                || tile.getType() == TileVisualType.ICON_DEFAULT) {
-            params.width = resources.getDimensionPixelSize(R.dimen.tile_view_monogram_size_modern);
-            params.height = resources.getDimensionPixelSize(R.dimen.tile_view_monogram_size_modern);
-            params.topMargin =
-                    resources.getDimensionPixelSize(R.dimen.tile_view_monogram_margin_top_modern);
-        } else {
-            params.width = resources.getDimensionPixelSize(R.dimen.tile_view_icon_size_modern);
-            params.height = resources.getDimensionPixelSize(R.dimen.tile_view_icon_size_modern);
-            params.topMargin =
-                    resources.getDimensionPixelSize(R.dimen.tile_view_icon_margin_top_modern);
-        }
-        mIconView.setLayoutParams(params);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/Tile.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/Tile.java
deleted file mode 100644
index c7a3ef5f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/Tile.java
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-
-import org.chromium.chrome.browser.favicon.IconType;
-
-/**
- * Holds the details to populate a site suggestion tile.
- */
-public class Tile implements OfflinableSuggestion {
-    private final SiteSuggestion mSiteData;
-
-    private final int mIndex;
-
-    @TileVisualType
-    private int mType = TileVisualType.NONE;
-
-    @IconType
-    private int mIconType = IconType.INVALID;
-
-    @Nullable
-    private Drawable mIcon;
-
-    @Nullable
-    private Long mOfflinePageOfflineId;
-
-    /**
-     * @param suggestion The site data we want to populate the tile with.
-     * @param index The index of this tile in the list of tiles.
-     */
-    public Tile(SiteSuggestion suggestion, int index) {
-        mSiteData = suggestion;
-        mIndex = index;
-    }
-
-    public SiteSuggestion getData() {
-        return mSiteData;
-    }
-
-    @Override
-    public String getUrl() {
-        return mSiteData.url;
-    }
-
-    @Override
-    public void setOfflinePageOfflineId(@Nullable Long offlineId) {
-        mOfflinePageOfflineId = offlineId;
-    }
-
-    @Nullable
-    @Override
-    public Long getOfflinePageOfflineId() {
-        return mOfflinePageOfflineId;
-    }
-
-    @Override
-    public boolean requiresExactOfflinePage() {
-        return false;
-    }
-
-    /**
-     * @return The title of this tile.
-     */
-    public String getTitle() {
-        return mSiteData.title;
-    }
-
-    /**
-     * @return Whether this tile is available offline.
-     */
-    public boolean isOfflineAvailable() {
-        return getOfflinePageOfflineId() != null;
-    }
-
-    /**
-     * @return The index of this tile in the list of tiles.
-     */
-    public int getIndex() {
-        return mIndex;
-    }
-
-    /**
-     * @return The source of this tile's title. Used for metrics tracking. Valid values are listed
-     * in {@code TileTitleSource}.
-     */
-    @TileTitleSource
-    public int getTitleSource() {
-        return mSiteData.titleSource;
-    }
-
-    /**
-     * @return The source of this tile. Used for metrics tracking. Valid values are listed in
-     * {@code TileSource}.
-     */
-    @TileSource
-    public int getSource() {
-        return mSiteData.source;
-    }
-
-    /**
-     * @return The visual type of this tile. Valid values are listed in {@link TileVisualType}.
-     */
-    @TileVisualType
-    public int getType() {
-        return mType;
-    }
-
-    /**
-     * Sets the visual type of this tile. Valid values are listed in
-     * {@link TileVisualType}.
-     */
-    public void setType(@TileVisualType int type) {
-        mType = type;
-    }
-
-    /**
-     * @return The icon type of this tile. Valid values are listed in {@link IconType}.
-     */
-    @IconType
-    public int getIconType() {
-        return mIconType;
-    }
-
-    /**
-     * Sets the icon type of this tile. Valid values are listed in {@link IconType}.
-     */
-    public void setIconType(@IconType int iconType) {
-        mIconType = iconType;
-    }
-
-    /**
-     * @return The icon, may be null.
-     */
-    @Nullable
-    public Drawable getIcon() {
-        return mIcon;
-    }
-
-    /**
-     * Updates the icon drawable.
-     */
-    public void setIcon(@Nullable Drawable icon) {
-        mIcon = icon;
-    }
-
-    @TileSectionType
-    public int getSectionType() {
-        return mSiteData.sectionType;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridLayout.java
deleted file mode 100644
index c9cd11e..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridLayout.java
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.util.MathUtils;
-
-/**
- * A layout that arranges tiles in a grid.
- */
-public class TileGridLayout extends FrameLayout {
-    private final int mVerticalSpacing;
-    private final int mMinHorizontalSpacing;
-    private final int mMaxHorizontalSpacing;
-    private final int mMaxWidth;
-
-    private int mMaxRows;
-    private int mMaxColumns;
-
-    /**
-     * Constructor for inflating from XML.
-     *
-     * @param context The view context in which this item will be shown.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     */
-    public TileGridLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        Resources res = getResources();
-        mVerticalSpacing = res.getDimensionPixelOffset(R.dimen.tile_grid_layout_vertical_spacing);
-        TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.TileGridLayout);
-        mMinHorizontalSpacing = styledAttrs.getDimensionPixelOffset(
-                R.styleable.TileGridLayout_minHorizontalSpacing,
-                res.getDimensionPixelOffset(R.dimen.tile_grid_layout_min_horizontal_spacing));
-        styledAttrs.recycle();
-        mMaxHorizontalSpacing = Integer.MAX_VALUE;
-        mMaxWidth = Integer.MAX_VALUE;
-    }
-
-    /**
-     * Sets the maximum number of rows to display. Any items that don't fit will be hidden.
-     */
-    public void setMaxRows(int rows) {
-        mMaxRows = rows;
-    }
-
-    /**
-     * Sets the maximum number of columns to display. Any items that don't fit will be hidden.
-     */
-    public void setMaxColumns(int columns) {
-        mMaxColumns = columns;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int totalWidth = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
-        int childCount = getChildCount();
-        if (childCount == 0) {
-            setMeasuredDimension(totalWidth, resolveSize(0, heightMeasureSpec));
-            return;
-        }
-
-        // Measure the children. We don't use the ViewGroup.measureChildren() method here because
-        // it only measures visible children. In a situation where a child is invisible before
-        // this measurement and we decide to show it after the measurement, it will not have its
-        // dimensions and will not be displayed.
-        for (int i = 0; i < childCount; i++) {
-            measureChild(getChildAt(i), MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-        }
-
-        // Determine the number of columns that will fit.
-        int childHeight = getChildAt(0).getMeasuredHeight();
-        int childWidth = getChildAt(0).getMeasuredWidth();
-        int numColumns = MathUtils.clamp(
-                (totalWidth + mMinHorizontalSpacing) / (childWidth + mMinHorizontalSpacing), 1,
-                mMaxColumns);
-
-        // Determine how much padding to use between and around the tiles.
-        int gridWidthMinusColumns = Math.max(0, totalWidth - numColumns * childWidth);
-        Pair<Integer, Integer> gridProperties =
-                computeHorizontalDimensions(true, gridWidthMinusColumns, numColumns);
-        int gridStart = gridProperties.first;
-        int horizontalSpacing = gridProperties.second;
-
-        // Limit the number of rows to mMaxRows.
-        int visibleChildCount = Math.min(childCount, mMaxRows * numColumns);
-
-        // Arrange the visible children in a grid.
-        int numRows = (visibleChildCount + numColumns - 1) / numColumns;
-        int paddingTop = getPaddingTop();
-        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-
-        for (int i = 0; i < visibleChildCount; i++) {
-            View child = getChildAt(i);
-            child.setVisibility(View.VISIBLE);
-            int row = i / numColumns;
-            int column = i % numColumns;
-            int childTop = row * (childHeight + mVerticalSpacing);
-            int childStart = gridStart + (column * (childWidth + horizontalSpacing));
-            MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
-            layoutParams.setMargins(isRtl ? 0 : childStart, childTop, isRtl ? childStart : 0, 0);
-            child.setLayoutParams(layoutParams);
-        }
-
-        // Hide any extra children in case there are more than needed for the maximum number of
-        // rows.
-        for (int i = visibleChildCount; i < childCount; i++) {
-            getChildAt(i).setVisibility(View.GONE);
-        }
-
-        int totalHeight = paddingTop + getPaddingBottom() + numRows * childHeight
-                + (numRows - 1) * mVerticalSpacing;
-
-        setMeasuredDimension(totalWidth, resolveSize(totalHeight, heightMeasureSpec));
-    }
-
-    /**
-     * @param spreadTiles Whether to spread the tiles with the same space between and around them.
-     * @param availableWidth The space available to spread between and around the tiles.
-     * @param numColumns The number of columns to be organised.
-     * @return The [gridStart, horizontalSpacing] pair of dimensions.
-     */
-    @VisibleForTesting
-    Pair<Integer, Integer> computeHorizontalDimensions(
-            boolean spreadTiles, int availableWidth, int numColumns) {
-        int gridStart;
-        float horizontalSpacing;
-        if (spreadTiles) {
-            // Identically sized spacers are added both between and around the tiles.
-            int spacerCount = numColumns + 1;
-            horizontalSpacing = (float) availableWidth / spacerCount;
-            gridStart = Math.round(horizontalSpacing);
-            if (horizontalSpacing < mMinHorizontalSpacing) {
-                return computeHorizontalDimensions(false, availableWidth, numColumns);
-            }
-        } else {
-            // Ensure column spacing isn't greater than mMaxHorizontalSpacing.
-            long gridSidePadding = availableWidth - (long) mMaxHorizontalSpacing * (numColumns - 1);
-            if (gridSidePadding > 0) {
-                horizontalSpacing = mMaxHorizontalSpacing;
-                gridStart = (int) (gridSidePadding / 2);
-            } else {
-                horizontalSpacing = (float) availableWidth / Math.max(1, numColumns - 1);
-                gridStart = 0;
-            }
-        }
-
-        assert horizontalSpacing >= mMinHorizontalSpacing;
-        assert horizontalSpacing <= mMaxHorizontalSpacing;
-
-        return Pair.create(gridStart, Math.round(horizontalSpacing));
-    }
-
-    @Nullable
-    public SuggestionsTileView getTileView(SiteSuggestion suggestion) {
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            SuggestionsTileView tileView = (SuggestionsTileView) getChildAt(i);
-            if (suggestion.equals(tileView.getData())) return tileView;
-        }
-        return null;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridViewHolder.java
deleted file mode 100644
index 38ec806b..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridViewHolder.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.content.res.Resources;
-import android.view.ViewGroup;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-
-import java.util.List;
-/**
- * A {@link SiteSectionViewHolder} specialised in displaying sites as a simple grid of tiles,
- * through
- * {@link TileGridLayout}.
- */
-public class TileGridViewHolder extends SiteSectionViewHolder {
-    private final TileGridLayout mSectionView;
-
-    public TileGridViewHolder(ViewGroup view, int maxRows, int maxColumns, UiConfig uiConfig) {
-        super(view);
-
-        mSectionView = (TileGridLayout) itemView;
-        mSectionView.setMaxRows(maxRows);
-        mSectionView.setMaxColumns(maxColumns);
-
-        Resources res = itemView.getResources();
-        int defaultLateralMargin =
-                res.getDimensionPixelSize(R.dimen.tile_grid_layout_padding_start);
-        int wideLateralMargin = res.getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins);
-    }
-
-    @Override
-    public void refreshData() {
-        assert mTileGroup.getTileSections().size() == 1;
-        List<Tile> tiles = mTileGroup.getTileSections().get(TileSectionType.PERSONALIZED);
-        assert tiles != null;
-
-        mTileRenderer.renderTileSection(tiles, mSectionView, mTileGroup.getTileSetupDelegate());
-        mTileGroup.notifyTilesRendered();
-    }
-
-    @Override
-    public SuggestionsTileView findTileView(SiteSuggestion data) {
-        return mSectionView.getTileView(data);
-    }
-
-    @Override
-    public void bindDataSource(TileGroup tileGroup, TileRenderer tileRenderer) {
-        super.bindDataSource(tileGroup, tileRenderer);
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java
deleted file mode 100644
index 28e66d8..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java
+++ /dev/null
@@ -1,606 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.graphics.Bitmap;
-import android.support.annotation.IntDef;
-import android.support.annotation.Nullable;
-import android.util.SparseArray;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnCreateContextMenuListener;
-
-import org.chromium.base.Callback;
-import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.favicon.IconType;
-import org.chromium.chrome.browser.favicon.LargeIconBridge;
-import org.chromium.chrome.browser.native_page.ContextMenuManager;
-import org.chromium.chrome.browser.native_page.ContextMenuManager.ContextMenuItemId;
-import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
-import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
-import org.chromium.ui.mojom.WindowOpenDisposition;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * The model and controller for a group of site suggestion tiles.
- */
-public class TileGroup implements MostVisitedSites.Observer {
-    /**
-     * Performs work in other parts of the system that the {@link TileGroup} should not know about.
-     */
-    public interface Delegate {
-        /**
-         * @param tile The tile corresponding to the most visited item to remove.
-         * @param removalUndoneCallback The callback to invoke if the removal is reverted. The
-         *                              callback's argument is the URL being restored.
-         */
-        void removeMostVisitedItem(Tile tile, Callback<String> removalUndoneCallback);
-
-        void openMostVisitedItem(int windowDisposition, Tile tile);
-
-        /**
-         * Gets the list of most visited sites.
-         * @param observer The observer to be notified with the list of sites.
-         * @param maxResults The maximum number of sites to retrieve.
-         */
-        void setMostVisitedSitesObserver(MostVisitedSites.Observer observer, int maxResults);
-
-        /**
-         * Called when the tile group has completely finished loading (all views will be inflated
-         * and any dependent resources will have been loaded).
-         * @param tiles The tiles owned by the {@link TileGroup}. Used to record metrics.
-         */
-        void onLoadingComplete(List<Tile> tiles);
-
-        /**
-         * To be called before this instance is abandoned to the garbage collector so it can do any
-         * necessary cleanups. This instance must not be used after this method is called.
-         */
-        void destroy();
-    }
-
-    /**
-     * An observer for events in the {@link TileGroup}.
-     */
-    public interface Observer {
-        /**
-         * Called when the tile group is initialised and when any of the tile data has changed,
-         * such as an icon, url, or title.
-         */
-        void onTileDataChanged();
-
-        /**
-         * Called when the number of tiles has changed.
-         */
-        void onTileCountChanged();
-
-        /**
-         * Called when a tile icon has changed.
-         * @param tile The tile for which the icon has changed.
-         */
-        void onTileIconChanged(Tile tile);
-
-        /**
-         * Called when the visibility of a tile's offline badge has changed.
-         * @param tile The tile for which the visibility of the offline badge has changed.
-         */
-        void onTileOfflineBadgeVisibilityChanged(Tile tile);
-    }
-
-    /**
-     * A delegate to allow {@link TileRenderer} to setup behaviours for the newly created views
-     * associated to a Tile.
-     */
-    public interface TileSetupDelegate {
-        /**
-         * Returns a delegate that will handle user interactions with the view created for the tile.
-         */
-        TileInteractionDelegate createInteractionDelegate(Tile tile);
-
-        /**
-         * Returns a callback to be invoked when the icon for the provided tile is loaded. It will
-         * be responsible for updating the tile data and triggering the visual refresh.
-         */
-        LargeIconBridge.LargeIconCallback createIconLoadCallback(Tile tile);
-    }
-
-    /**
-     * Constants used to track the current operations on the group and notify the {@link Delegate}
-     * when the expected sequence of potentially asynchronous operations is complete.
-     */
-    @VisibleForTesting
-    @IntDef({TileTask.FETCH_DATA, TileTask.SCHEDULE_ICON_FETCH, TileTask.FETCH_ICON})
-    @Retention(RetentionPolicy.SOURCE)
-    @interface TileTask {
-        /**
-         * An event that should result in new data being loaded happened.
-         * Can be an asynchronous task, spanning from when the {@link Observer} is registered to
-         * when the initial load completes.
-         */
-        int FETCH_DATA = 1;
-
-        /**
-         * New tile data has been loaded and we are expecting the related icons to be fetched.
-         * Can be an asynchronous task, as we rely on it being triggered by the embedder, some time
-         * after {@link Observer#onTileDataChanged()} is called.
-         */
-        int SCHEDULE_ICON_FETCH = 2;
-
-        /**
-         * The icon for a tile is being fetched.
-         * Asynchronous task, that is started for each icon that needs to be loaded.
-         */
-        int FETCH_ICON = 3;
-    }
-
-    private final SuggestionsUiDelegate mUiDelegate;
-    private final ContextMenuManager mContextMenuManager;
-    private final Delegate mTileGroupDelegate;
-    private final Observer mObserver;
-    private final TileRenderer mTileRenderer;
-
-    /**
-     * Tracks the tasks currently in flight.
-     *
-     * We only care about which ones are pending, not their order, and we can have multiple tasks
-     * pending of the same type. Hence exposing the type as Collection rather than List or Set.
-     */
-    private final Collection<Integer> mPendingTasks = new ArrayList<>();
-
-    /** Access point to offline related features. */
-    private final OfflineModelObserver mOfflineModelObserver;
-
-    /**
-     * Source of truth for the tile data. Avoid keeping a reference to a tile in long running
-     * callbacks, as it might be thrown out before it is called. Use URL or site data to look it up
-     * at the right time instead.
-     * @see #findTile(SiteSuggestion)
-     * @see #findTilesForUrl(String)
-     */
-    private SparseArray<List<Tile>> mTileSections = createEmptyTileData();
-
-    /** Most recently received tile data that has not been displayed yet. */
-    @Nullable
-    private List<SiteSuggestion> mPendingTiles;
-
-    /**
-     * URL of the most recently removed tile. Used to identify when a tile removal is confirmed by
-     * the tile backend.
-     */
-    @Nullable
-    private String mPendingRemovalUrl;
-
-    /**
-     * URL of the most recently added tile. Used to identify when a given tile's insertion is
-     * confirmed by the tile backend. This is relevant when a previously existing tile is removed,
-     * then the user undoes the action and wants that tile back.
-     */
-    @Nullable
-    private String mPendingInsertionUrl;
-
-    private boolean mHasReceivedData;
-
-    // TODO(dgn): Attempt to avoid cycling dependencies with TileRenderer. Is there a better way?
-    private final TileSetupDelegate mTileSetupDelegate = new TileSetupDelegate() {
-        @Override
-        public TileInteractionDelegate createInteractionDelegate(Tile tile) {
-            return new TileInteractionDelegate(tile.getData());
-        }
-
-        @Override
-        public LargeIconBridge.LargeIconCallback createIconLoadCallback(Tile tile) {
-            // TODO(dgn): We could save on fetches by avoiding a new one when there is one pending
-            // for the same URL, and applying the result to all matched URLs.
-            boolean trackLoad =
-                    isLoadTracked() && tile.getSectionType() == TileSectionType.PERSONALIZED;
-            if (trackLoad) addTask(TileTask.FETCH_ICON);
-            return new LargeIconCallbackImpl(tile.getData(), trackLoad);
-        }
-    };
-
-    /**
-     * @param tileRenderer Used to render icons.
-     * @param uiDelegate Delegate used to interact with the rest of the system.
-     * @param contextMenuManager Used to handle context menu invocations on the tiles.
-     * @param tileGroupDelegate Used for interactions with the Most Visited backend.
-     * @param observer Will be notified of changes to the tile data.
-     * @param offlinePageBridge Used to update the offline badge of the tiles.
-     */
-    public TileGroup(TileRenderer tileRenderer, SuggestionsUiDelegate uiDelegate,
-            ContextMenuManager contextMenuManager, Delegate tileGroupDelegate, Observer observer,
-            OfflinePageBridge offlinePageBridge) {
-        mUiDelegate = uiDelegate;
-        mContextMenuManager = contextMenuManager;
-        mTileGroupDelegate = tileGroupDelegate;
-        mObserver = observer;
-        mTileRenderer = tileRenderer;
-        mOfflineModelObserver = new OfflineModelObserver(offlinePageBridge);
-        mUiDelegate.addDestructionObserver(mOfflineModelObserver);
-    }
-
-    @Override
-    public void onSiteSuggestionsAvailable(List<SiteSuggestion> siteSuggestions) {
-        // Only transforms the incoming tiles and stores them in a buffer for when we decide to
-        // refresh the tiles in the UI.
-
-        boolean removalCompleted = mPendingRemovalUrl != null;
-        boolean insertionCompleted = mPendingInsertionUrl == null;
-
-        mPendingTiles = new ArrayList<>();
-        for (SiteSuggestion suggestion : siteSuggestions) {
-            mPendingTiles.add(suggestion);
-
-            // Only tiles in the personal section can be modified.
-            if (suggestion.sectionType != TileSectionType.PERSONALIZED) continue;
-            if (suggestion.url.equals(mPendingRemovalUrl)) removalCompleted = false;
-            if (suggestion.url.equals(mPendingInsertionUrl)) insertionCompleted = true;
-        }
-
-        boolean expectedChangeCompleted = false;
-        if (mPendingRemovalUrl != null && removalCompleted) {
-            mPendingRemovalUrl = null;
-            expectedChangeCompleted = true;
-        }
-        if (mPendingInsertionUrl != null && insertionCompleted) {
-            mPendingInsertionUrl = null;
-            expectedChangeCompleted = true;
-        }
-
-        if (!mHasReceivedData || !mUiDelegate.isVisible() || expectedChangeCompleted) loadTiles();
-    }
-
-    @Override
-    public void onIconMadeAvailable(String siteUrl) {
-        for (Tile tile : findTilesForUrl(siteUrl)) {
-            mTileRenderer.updateIcon(tile.getData(),
-                    new LargeIconCallbackImpl(tile.getData(), /* trackLoadTask = */ false));
-        }
-    }
-
-    /**
-     * Instructs this instance to start listening for data. The {@link TileGroup.Observer} may be
-     * called immediately if new data is received synchronously.
-     * @param maxResults The maximum number of sites to retrieve.
-     */
-    public void startObserving(int maxResults) {
-        addTask(TileTask.FETCH_DATA);
-        mTileGroupDelegate.setMostVisitedSitesObserver(this, maxResults);
-    }
-
-    /**
-     * Method to be called when a tile render has been triggered, to let the {@link TileGroup}
-     * update its internal task tracking status.
-     * @see Delegate#onLoadingComplete(List)
-     */
-    public void notifyTilesRendered() {
-        // Icon fetch scheduling was done when building the tile views.
-        if (isLoadTracked()) removeTask(TileTask.SCHEDULE_ICON_FETCH);
-    }
-
-    /** @return the sites currently loaded in the group, grouped by vertical. */
-    public SparseArray<List<Tile>> getTileSections() {
-        return mTileSections;
-    }
-
-    public boolean hasReceivedData() {
-        return mHasReceivedData;
-    }
-
-    /** @return Whether the group has no sites to display. */
-    public boolean isEmpty() {
-        for (int i = 0; i < mTileSections.size(); i++) {
-            if (!mTileSections.valueAt(i).isEmpty()) return false;
-        }
-        return true;
-    }
-
-    /**
-     * To be called when the view displaying the tile group becomes visible.
-     * @param trackLoadTask whether the delegate should be notified that the load is completed
-     *      through {@link Delegate#onLoadingComplete(List)}.
-     */
-    public void onSwitchToForeground(boolean trackLoadTask) {
-        if (trackLoadTask) addTask(TileTask.FETCH_DATA);
-        if (mPendingTiles != null) loadTiles();
-        if (trackLoadTask) removeTask(TileTask.FETCH_DATA);
-    }
-
-    /** Loads tile data from {@link #mPendingTiles} and clears it afterwards. */
-    private void loadTiles() {
-        assert mPendingTiles != null;
-
-        boolean isInitialLoad = !mHasReceivedData;
-        mHasReceivedData = true;
-
-        boolean dataChanged = isInitialLoad;
-        List<Tile> personalisedTiles = mTileSections.get(TileSectionType.PERSONALIZED);
-        int oldPersonalisedTilesCount = personalisedTiles == null ? 0 : personalisedTiles.size();
-
-        SparseArray<List<Tile>> newSites = createEmptyTileData();
-        for (int i = 0; i < mPendingTiles.size(); ++i) {
-            SiteSuggestion suggestion = mPendingTiles.get(i);
-            Tile tile = findTile(suggestion);
-            if (tile == null) {
-                dataChanged = true;
-                tile = new Tile(suggestion, i);
-            }
-
-            List<Tile> sectionTiles = newSites.get(suggestion.sectionType);
-            if (sectionTiles == null) {
-                sectionTiles = new ArrayList<>();
-                newSites.append(suggestion.sectionType, sectionTiles);
-            }
-
-            // This is not supposed to happen but does. See https://crbug.com/703628
-            if (findTile(suggestion.url, sectionTiles) != null) continue;
-
-            sectionTiles.add(tile);
-        }
-
-        mTileSections = newSites;
-        mPendingTiles = null;
-
-        // TODO(dgn): change these events, maybe introduce new ones or just change semantics? This
-        // will depend on the UI to be implemented and the desired refresh behaviour.
-        List<Tile> personalizedTiles = mTileSections.get(TileSectionType.PERSONALIZED);
-        int numberOfPersonalizedTiles = personalizedTiles == null ? 0 : personalizedTiles.size();
-        boolean countChanged =
-                isInitialLoad || numberOfPersonalizedTiles != oldPersonalisedTilesCount;
-        dataChanged = dataChanged || countChanged;
-
-        if (!dataChanged) return;
-
-        mOfflineModelObserver.updateAllSuggestionsOfflineAvailability(
-                /* reportPrefetchedSuggestionsCount = */ false);
-
-        if (countChanged) mObserver.onTileCountChanged();
-
-        if (isLoadTracked()) addTask(TileTask.SCHEDULE_ICON_FETCH);
-        mObserver.onTileDataChanged();
-
-        if (isInitialLoad) removeTask(TileTask.FETCH_DATA);
-    }
-
-    @Nullable
-    private Tile findTile(SiteSuggestion suggestion) {
-        if (mTileSections.get(suggestion.sectionType) == null) return null;
-        for (Tile tile : mTileSections.get(suggestion.sectionType)) {
-            if (tile.getData().equals(suggestion)) return tile;
-        }
-        return null;
-    }
-
-    /**
-     * @param url The URL to search for.
-     * @param tiles The section to search in, represented by the contained list of tiles.
-     * @return A tile matching the provided URL and section, or {@code null} if none is found. */
-    private Tile findTile(String url, @Nullable List<Tile> tiles) {
-        if (tiles == null) return null;
-        for (Tile tile : tiles) {
-            if (tile.getUrl().equals(url)) return tile;
-        }
-        return null;
-    }
-
-    /** @return All tiles matching the provided URL, or an empty list if none is found. */
-    private List<Tile> findTilesForUrl(String url) {
-        List<Tile> tiles = new ArrayList<>();
-        for (int i = 0; i < mTileSections.size(); ++i) {
-            for (Tile tile : mTileSections.valueAt(i)) {
-                if (tile.getUrl().equals(url)) tiles.add(tile);
-            }
-        }
-        return tiles;
-    }
-
-    private void addTask(@TileTask int task) {
-        mPendingTasks.add(task);
-    }
-
-    private void removeTask(@TileTask int task) {
-        boolean removedTask = mPendingTasks.remove(Integer.valueOf(task));
-        assert removedTask;
-
-        if (mPendingTasks.isEmpty()) {
-            // TODO(dgn): We only notify about the personal tiles because that's the only ones we
-            // wait for to be loaded. We also currently rely on the tile order in the returned
-            // array as the reported position in UMA, but this is not accurate and would be broken
-            // if we returned all the tiles regardless of sections.
-            List<Tile> personalTiles = mTileSections.get(TileSectionType.PERSONALIZED);
-            assert personalTiles != null;
-            mTileGroupDelegate.onLoadingComplete(personalTiles);
-        }
-    }
-
-    /**
-     * @return Whether the current load is being tracked. Unrequested task tracking updates should
-     * not be sent, as it would cause calling {@link Delegate#onLoadingComplete(List)} at the
-     * wrong moment.
-     */
-    private boolean isLoadTracked() {
-        return mPendingTasks.contains(TileTask.FETCH_DATA)
-                || mPendingTasks.contains(TileTask.SCHEDULE_ICON_FETCH);
-    }
-
-    @VisibleForTesting
-    boolean isTaskPending(@TileTask int task) {
-        return mPendingTasks.contains(task);
-    }
-
-    @VisibleForTesting
-    TileSetupDelegate getTileSetupDelegate() {
-        return mTileSetupDelegate;
-    }
-
-    @Nullable
-    public SiteSuggestion getHomepageTileData() {
-        for (Tile tile : mTileSections.get(TileSectionType.PERSONALIZED)) {
-            if (tile.getSource() == TileSource.HOMEPAGE) {
-                return tile.getData();
-            }
-        }
-        return null;
-    }
-
-    private static SparseArray<List<Tile>> createEmptyTileData() {
-        SparseArray<List<Tile>> newTileData = new SparseArray<>();
-
-        // TODO(dgn): How do we want to handle empty states and sections that have no tiles?
-        // Have an empty list for now that can be rendered as-is without causing issues or too much
-        // state checking. We will have to decide if we want empty lists or no section at all for
-        // the others.
-        newTileData.put(TileSectionType.PERSONALIZED, new ArrayList<Tile>());
-
-        return newTileData;
-    }
-
-    // TODO(dgn): I would like to move that to TileRenderer, but setting the data on the tile,
-    // notifying the observer and updating the tasks make it awkward.
-    private class LargeIconCallbackImpl implements LargeIconBridge.LargeIconCallback {
-        private final SiteSuggestion mSiteData;
-        private final boolean mTrackLoadTask;
-
-        private LargeIconCallbackImpl(SiteSuggestion suggestion, boolean trackLoadTask) {
-            mSiteData = suggestion;
-            mTrackLoadTask = trackLoadTask;
-        }
-
-        @Override
-        public void onLargeIconAvailable(@Nullable Bitmap icon, int fallbackColor,
-                boolean isFallbackColorDefault, @IconType int iconType) {
-            Tile tile = findTile(mSiteData);
-            if (tile != null) { // Do nothing if the tile was removed.
-                tile.setIconType(iconType);
-                if (icon == null) {
-                    mTileRenderer.setTileIconFromColor(tile, fallbackColor, isFallbackColorDefault);
-                } else {
-                    mTileRenderer.setTileIconFromBitmap(tile, icon);
-                }
-
-                mObserver.onTileIconChanged(tile);
-            }
-
-            // This call needs to be made after the tiles are completely initialised, for UMA.
-            if (mTrackLoadTask) removeTask(TileTask.FETCH_ICON);
-        }
-    }
-
-    /**
-     * Implements various listener and delegate interfaces to handle user interactions with tiles.
-     */
-    public class TileInteractionDelegate
-            implements ContextMenuManager.Delegate, OnClickListener, OnCreateContextMenuListener {
-        private final SiteSuggestion mSuggestion;
-        private Runnable mOnClickRunnable;
-
-        public TileInteractionDelegate(SiteSuggestion suggestion) {
-            mSuggestion = suggestion;
-        }
-
-        @Override
-        public void onClick(View view) {
-            Tile tile = findTile(mSuggestion);
-            if (tile == null) return;
-
-            SuggestionsMetrics.recordTileTapped();
-            if (mOnClickRunnable != null) mOnClickRunnable.run();
-            mTileGroupDelegate.openMostVisitedItem(WindowOpenDisposition.CURRENT_TAB, tile);
-        }
-
-        @Override
-        public void openItem(int windowDisposition) {
-            Tile tile = findTile(mSuggestion);
-            if (tile == null) return;
-
-            mTileGroupDelegate.openMostVisitedItem(windowDisposition, tile);
-        }
-
-        @Override
-        public void removeItem() {
-            Tile tile = findTile(mSuggestion);
-            if (tile == null) return;
-
-            // Note: This does not track all the removals, but will track the most recent one. If
-            // that removal is committed, it's good enough for change detection.
-            mPendingRemovalUrl = mSuggestion.url;
-            mTileGroupDelegate.removeMostVisitedItem(tile, url -> mPendingInsertionUrl = url);
-        }
-
-        @Override
-        public String getUrl() {
-            return mSuggestion.url;
-        }
-
-        @Override
-        public String getContextMenuTitle() {
-            return null;
-        }
-
-        @Override
-        public boolean isItemSupported(@ContextMenuItemId int menuItemId) {
-            switch (menuItemId) {
-                // Personalized tiles are the only tiles that can be removed.  Additionally, the
-                // Explore tile counts as a personalized tile but cannot be removed.
-                case ContextMenuItemId.REMOVE:
-                    return mSuggestion.sectionType == TileSectionType.PERSONALIZED
-                            && mSuggestion.source != TileSource.EXPLORE;
-                case ContextMenuItemId.LEARN_MORE:
-                    return SuggestionsConfig.scrollToLoad();
-                case ContextMenuItemId.OPEN_IN_INCOGNITO_TAB:
-                    return mSuggestion.source != TileSource.EXPLORE;
-                default:
-                    return true;
-            }
-        }
-
-        @Override
-        public void onContextMenuCreated() {}
-
-        @Override
-        public void onCreateContextMenu(
-                ContextMenu contextMenu, View view, ContextMenuInfo contextMenuInfo) {
-            mContextMenuManager.createContextMenu(contextMenu, view, this);
-        }
-
-        /**
-         * Set a runnable for click events on the tile. This is primarily used to track interaction
-         * with the tile used by feature engagement purposes.
-         * @param clickRunnable The {@link Runnable} to be executed when tile is clicked.
-         */
-        public void setOnClickRunnable(Runnable clickRunnable) {
-            mOnClickRunnable = clickRunnable;
-        }
-    }
-
-    private class OfflineModelObserver extends SuggestionsOfflineModelObserver<Tile> {
-        public OfflineModelObserver(OfflinePageBridge bridge) {
-            super(bridge);
-        }
-
-        @Override
-        public void onSuggestionOfflineIdChanged(Tile tile, OfflinePageItem item) {
-            boolean oldOfflineAvailable = tile.isOfflineAvailable();
-            tile.setOfflinePageOfflineId(item == null ? null : item.getOfflineId());
-
-            // Only notify to update the view if there will be a visible change.
-            if (oldOfflineAvailable == tile.isOfflineAvailable()) return;
-            mObserver.onTileOfflineBadgeVisibilityChanged(tile);
-        }
-
-        @Override
-        public Iterable<Tile> getOfflinableSuggestions() {
-            List<Tile> tiles = new ArrayList<>();
-            for (int i = 0; i < mTileSections.size(); ++i) tiles.addAll(mTileSections.valueAt(i));
-            return tiles;
-        }
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java
deleted file mode 100644
index 7c517e9a..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.content.Context;
-
-import org.chromium.base.Callback;
-import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ntp.NewTabPageUma;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.snackbar.Snackbar;
-import org.chromium.chrome.browser.snackbar.SnackbarManager;
-import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
-import org.chromium.ui.mojom.WindowOpenDisposition;
-
-import java.util.List;
-
-/**
- * Reusable implementation of {@link TileGroup.Delegate}. Performs work in parts of the system that
- * the {@link TileGroup} should not know about.
- */
-public class TileGroupDelegateImpl implements TileGroup.Delegate {
-
-    private final Context mContext;
-    private final SnackbarManager mSnackbarManager;
-    private final SuggestionsNavigationDelegate mNavigationDelegate;
-    private final MostVisitedSites mMostVisitedSites;
-
-    private boolean mIsDestroyed;
-    private SnackbarController mTileRemovedSnackbarController;
-
-    public TileGroupDelegateImpl(ChromeActivity activity, Profile profile,
-            SuggestionsNavigationDelegate navigationDelegate, SnackbarManager snackbarManager) {
-        mContext = activity;
-        mSnackbarManager = snackbarManager;
-        mNavigationDelegate = navigationDelegate;
-        mMostVisitedSites =
-                SuggestionsDependencyFactory.getInstance().createMostVisitedSites(profile);
-    }
-
-    @Override
-    public void removeMostVisitedItem(Tile item, Callback<String> removalUndoneCallback) {
-        assert !mIsDestroyed;
-
-        mMostVisitedSites.addBlacklistedUrl(item.getUrl());
-        showTileRemovedSnackbar(item.getUrl(), removalUndoneCallback);
-    }
-
-    @Override
-    public void openMostVisitedItem(int windowDisposition, Tile item) {
-        assert !mIsDestroyed;
-
-        String url = item.getUrl();
-
-        // TODO(treib): Should we call recordOpenedMostVisitedItem here?
-        if (windowDisposition != WindowOpenDisposition.NEW_WINDOW) {
-            recordOpenedTile(item);
-        }
-
-        mNavigationDelegate.navigateToSuggestionUrl(windowDisposition, url);
-    }
-
-    @Override
-    public void setMostVisitedSitesObserver(MostVisitedSites.Observer observer, int maxResults) {
-        assert !mIsDestroyed;
-
-        mMostVisitedSites.setObserver(observer, maxResults);
-    }
-
-    @Override
-    public void onLoadingComplete(List<Tile> tiles) {
-        // This method is called after network calls complete. It could happen after the suggestions
-        // surface is destroyed.
-        if (mIsDestroyed) return;
-
-        for (Tile tile : tiles) {
-            mMostVisitedSites.recordTileImpression(tile);
-        }
-
-        mMostVisitedSites.recordPageImpression(tiles.size());
-
-        for (Tile tile : tiles) {
-            if (tile.isOfflineAvailable()) {
-                SuggestionsMetrics.recordTileOfflineAvailability(tile.getIndex());
-            }
-        }
-    }
-
-    @Override
-    public void destroy() {
-        assert !mIsDestroyed;
-        mIsDestroyed = true;
-
-        if (mTileRemovedSnackbarController != null) {
-            mSnackbarManager.dismissSnackbars(mTileRemovedSnackbarController);
-        }
-        mMostVisitedSites.destroy();
-    }
-
-    private void showTileRemovedSnackbar(String url, final Callback<String> removalUndoneCallback) {
-        if (mTileRemovedSnackbarController == null) {
-            mTileRemovedSnackbarController = new SnackbarController() {
-                @Override
-                public void onDismissNoAction(Object actionData) {}
-
-                /** Undoes the tile removal. */
-                @Override
-                public void onAction(Object actionData) {
-                    if (mIsDestroyed) return;
-                    String url = (String) actionData;
-                    removalUndoneCallback.onResult(url);
-                    mMostVisitedSites.removeBlacklistedUrl(url);
-                }
-            };
-        }
-        Snackbar snackbar = Snackbar.make(mContext.getString(R.string.most_visited_item_removed),
-                                            mTileRemovedSnackbarController, Snackbar.TYPE_ACTION,
-                                            Snackbar.UMA_NTP_MOST_VISITED_DELETE_UNDO)
-                                    .setAction(mContext.getString(R.string.undo), url);
-        mSnackbarManager.showSnackbar(snackbar);
-    }
-
-    private void recordOpenedTile(Tile tile) {
-        NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_MOST_VISITED_TILE);
-        RecordUserAction.record("MobileNTPMostVisited");
-        NewTabPageUma.recordExplicitUserNavigation(
-                tile.getUrl(), NewTabPageUma.RAPPOR_ACTION_VISITED_SUGGESTED_TILE);
-        mMostVisitedSites.recordOpenedMostVisitedItem(tile);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java
deleted file mode 100644
index b916b01..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java
+++ /dev/null
@@ -1,283 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.support.annotation.LayoutRes;
-import android.support.graphics.drawable.VectorDrawableCompat;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.Log;
-import org.chromium.base.VisibleForTesting;
-import org.chromium.base.task.AsyncTask;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
-import org.chromium.chrome.browser.explore_sites.ExploreSitesIPH;
-import org.chromium.chrome.browser.explore_sites.MostLikelyVariation;
-import org.chromium.chrome.browser.favicon.IconType;
-import org.chromium.chrome.browser.favicon.LargeIconBridge;
-import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle;
-import org.chromium.chrome.browser.suggestions.tile.TopSitesTileView;
-import org.chromium.chrome.browser.util.ViewUtils;
-import org.chromium.chrome.browser.widget.RoundedIconGenerator;
-import org.chromium.components.feature_engagement.EventConstants;
-import org.chromium.components.feature_engagement.Tracker;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Utility class that renders {@link Tile}s into a provided {@link ViewGroup}, creating and
- * manipulating the views as needed.
- */
-public class TileRenderer {
-    private static final String TAG = "TileRenderer";
-
-    private final Resources mResources;
-    private final ImageFetcher mImageFetcher;
-    private final RoundedIconGenerator mIconGenerator;
-    private final Resources.Theme mTheme;
-
-    @TileStyle
-    private final int mStyle;
-    private final int mTitleLinesCount;
-    private final int mDesiredIconSize;
-    private final int mMinIconSize;
-    private final float mIconCornerRadius;
-
-    @LayoutRes
-    private final int mLayout;
-
-    @LayoutRes
-    private final int mTopSitesLayout;
-
-    public TileRenderer(
-            Context context, @TileStyle int style, int titleLines, ImageFetcher imageFetcher) {
-        mImageFetcher = imageFetcher;
-        mStyle = style;
-        mTitleLinesCount = titleLines;
-
-        mResources = context.getResources();
-        mTheme = context.getTheme();
-        mDesiredIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_size);
-        mIconCornerRadius = mResources.getDimension(R.dimen.tile_view_icon_corner_radius);
-        int minIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_min_size);
-
-        // On ldpi devices, mDesiredIconSize could be even smaller than the global limit.
-        mMinIconSize = Math.min(mDesiredIconSize, minIconSize);
-
-        mLayout = getLayout();
-        mTopSitesLayout = getTopSitesLayout();
-
-        int iconColor = ApiCompatibilityUtils.getColor(
-                mResources, R.color.default_favicon_background_color);
-        int iconTextSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_text_size);
-        mIconGenerator = new RoundedIconGenerator(
-                mDesiredIconSize, mDesiredIconSize, mDesiredIconSize / 2, iconColor, iconTextSize);
-    }
-
-    /**
-     * Renders tile views in the given {@link ViewGroup}, reusing existing tile views where
-     * possible because view inflation and icon loading are slow.
-     * @param parent The layout to render the tile views into.
-     * @param sectionTiles Tiles to render.
-     * @param setupDelegate Delegate used to setup callbacks and listeners for the new views.
-     */
-    public void renderTileSection(
-            List<Tile> sectionTiles, ViewGroup parent, TileGroup.TileSetupDelegate setupDelegate) {
-        // Map the old tile views by url so they can be reused later.
-        Map<SiteSuggestion, SuggestionsTileView> oldTileViews = new HashMap<>();
-        int childCount = parent.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            SuggestionsTileView tileView = (SuggestionsTileView) parent.getChildAt(i);
-            oldTileViews.put(tileView.getData(), tileView);
-        }
-
-        // Remove all views from the layout because even if they are reused later they'll have to be
-        // added back in the correct order.
-        parent.removeAllViews();
-
-        for (Tile tile : sectionTiles) {
-            SuggestionsTileView tileView = oldTileViews.get(tile.getData());
-            if (tileView == null) {
-                tileView = buildTileView(tile, parent, setupDelegate);
-            }
-
-            parent.addView(tileView);
-        }
-    }
-
-    /**
-     * Record that a tile was clicked for IPH reasons.
-     */
-    private void recordTileClickedForIPH(String eventName) {
-        Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
-        tracker.notifyEvent(eventName);
-    }
-
-    /**
-     * Inflates a new tile view, initializes it, and loads an icon for it.
-     * @param tile The tile that holds the data to populate the new tile view.
-     * @param parentView The parent of the new tile view.
-     * @param setupDelegate The delegate used to setup callbacks and listeners for the new view.
-     * @return The new tile view.
-     */
-    @VisibleForTesting
-    SuggestionsTileView buildTileView(
-            Tile tile, ViewGroup parentView, TileGroup.TileSetupDelegate setupDelegate) {
-        SuggestionsTileView tileView;
-
-        if (tile.getSource() == TileSource.EXPLORE) {
-            tileView = (TopSitesTileView) LayoutInflater.from(parentView.getContext())
-                               .inflate(mTopSitesLayout, parentView, false);
-
-            int iconVariation = ExploreSitesBridge.getIconVariation();
-            if (iconVariation == MostLikelyVariation.ICON_ARROW) {
-                tile.setIcon(VectorDrawableCompat.create(
-                        mResources, R.drawable.ic_arrow_forward_blue_24dp, mTheme));
-                tile.setType(TileVisualType.ICON_REAL);
-            } else if (iconVariation == MostLikelyVariation.ICON_DOTS) {
-                tile.setIcon(VectorDrawableCompat.create(
-                        mResources, R.drawable.ic_apps_blue_24dp, mTheme));
-                tile.setType(TileVisualType.ICON_REAL);
-            } else if (iconVariation == MostLikelyVariation.ICON_GROUPED) {
-                tile.setIcon(VectorDrawableCompat.create(
-                        mResources, R.drawable.ic_apps_blue_24dp, mTheme));
-                tile.setType(TileVisualType.ICON_DEFAULT);
-
-                // One task to load actual icon.
-                LargeIconBridge.LargeIconCallback bridgeCallback =
-                        setupDelegate.createIconLoadCallback(tile);
-                ExploreSitesBridge.getSummaryImage(Profile.getLastUsedProfile(), mDesiredIconSize,
-                        (Bitmap img)
-                                -> bridgeCallback.onLargeIconAvailable(
-                                        img, Color.BLACK, false, IconType.FAVICON));
-            }
-        } else {
-            tileView = (SuggestionsTileView) LayoutInflater.from(parentView.getContext())
-                               .inflate(mLayout, parentView, false);
-        }
-
-        tileView.initialize(tile, mTitleLinesCount);
-
-        // Note: It is important that the callbacks below don't keep a reference to the tile or
-        // modify them as there is no guarantee that the same tile would be used to update the view.
-        if (tile.getSource() != TileSource.EXPLORE) {
-            fetchIcon(tile.getData(), setupDelegate.createIconLoadCallback(tile));
-        }
-
-        TileGroup.TileInteractionDelegate delegate = setupDelegate.createInteractionDelegate(tile);
-        if (tile.getSource() == TileSource.HOMEPAGE) {
-            delegate.setOnClickRunnable(
-                    () -> recordTileClickedForIPH(EventConstants.HOMEPAGE_TILE_CLICKED));
-        } else if (tile.getSource() == TileSource.EXPLORE) {
-            delegate.setOnClickRunnable(
-                    () -> recordTileClickedForIPH(EventConstants.EXPLORE_SITES_TILE_TAPPED));
-        }
-
-        tileView.setOnClickListener(delegate);
-        tileView.setOnCreateContextMenuListener(delegate);
-
-        if (tile.getSource() == TileSource.EXPLORE) {
-            ExploreSitesIPH.configureIPH(tileView, Profile.getLastUsedProfile());
-        }
-
-        return tileView;
-    }
-
-    private void fetchIcon(
-            final SiteSuggestion siteData, final LargeIconBridge.LargeIconCallback iconCallback) {
-        if (siteData.whitelistIconPath.isEmpty()) {
-            mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback);
-            return;
-        }
-
-        AsyncTask<Bitmap> task = new AsyncTask<Bitmap>() {
-            @Override
-            protected Bitmap doInBackground() {
-                Bitmap bitmap = BitmapFactory.decodeFile(siteData.whitelistIconPath);
-                if (bitmap == null) {
-                    Log.d(TAG, "Image decoding failed: %s", siteData.whitelistIconPath);
-                }
-                return bitmap;
-            }
-
-            @Override
-            protected void onPostExecute(Bitmap icon) {
-                if (icon == null) {
-                    mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback);
-                } else {
-                    iconCallback.onLargeIconAvailable(icon, Color.BLACK, false, IconType.INVALID);
-                }
-            }
-        };
-        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-    }
-
-    public void updateIcon(
-            SiteSuggestion siteData, LargeIconBridge.LargeIconCallback iconCallback) {
-        mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback);
-    }
-
-    public void setTileIconFromBitmap(Tile tile, Bitmap icon) {
-        int radius = Math.round(mIconCornerRadius * icon.getWidth() / mDesiredIconSize);
-        if (tile.getSource() == TileSource.EXPLORE) {
-            radius = mDesiredIconSize / 2;
-        }
-        RoundedBitmapDrawable roundedIcon = ViewUtils.createRoundedBitmapDrawable(icon, radius);
-        roundedIcon.setAntiAlias(true);
-        roundedIcon.setFilterBitmap(true);
-
-        tile.setIcon(roundedIcon);
-        tile.setType(TileVisualType.ICON_REAL);
-    }
-
-    public void setTileIconFromColor(Tile tile, int fallbackColor, boolean isFallbackColorDefault) {
-        // Explore should not have generated icons.
-        if (tile.getSource() == TileSource.EXPLORE) {
-            return;
-        }
-        mIconGenerator.setBackgroundColor(fallbackColor);
-        Bitmap icon = mIconGenerator.generateIconForUrl(tile.getUrl());
-        tile.setIcon(new BitmapDrawable(mResources, icon));
-        tile.setType(
-                isFallbackColorDefault ? TileVisualType.ICON_DEFAULT : TileVisualType.ICON_COLOR);
-    }
-
-    @LayoutRes
-    private int getLayout() {
-        switch (mStyle) {
-            case TileStyle.MODERN:
-                return R.layout.suggestions_tile_view;
-            case TileStyle.MODERN_CONDENSED:
-                return R.layout.suggestions_tile_view_condensed;
-        }
-        assert false;
-        return 0;
-    }
-
-    @LayoutRes
-    private int getTopSitesLayout() {
-        switch (mStyle) {
-            case TileStyle.MODERN:
-                return R.layout.top_sites_tile_view;
-            case TileStyle.MODERN_CONDENSED:
-                return R.layout.top_sites_tile_view_condensed;
-        }
-        assert false;
-        return 0;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java
new file mode 100644
index 0000000..9c345f9f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java
@@ -0,0 +1,94 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.mostvisited;
+
+import android.support.annotation.Nullable;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.suggestions.tile.Tile;
+
+import java.util.List;
+
+/**
+ * Methods to provide most recent urls, titles and thumbnails.
+ */
+public interface MostVisitedSites {
+    /**
+     * An interface for handling events in {@link MostVisitedSites}.
+     */
+    interface Observer {
+        /** This is called when the list of most visited URLs is initially available or updated. */
+        void onSiteSuggestionsAvailable(List<SiteSuggestion> siteSuggestions);
+
+        /**
+         * This is called when a previously uncached icon has been fetched.
+         * Parameters guaranteed to be non-null.
+         *
+         * @param siteUrl URL of site with newly-cached icon.
+         */
+        void onIconMadeAvailable(String siteUrl);
+    }
+
+    /**
+     * An interface to provide {@link MostVisitedSites} with platform-specific home page data.
+     */
+    interface HomepageClient {
+        /**
+         * @return True if homepage tile should be shown.
+         */
+        @CalledByNative("HomepageClient")
+        boolean isHomepageTileEnabled();
+
+        /**
+         * @return The raw URL of the currently set home page.
+         */
+        @CalledByNative("HomepageClient")
+        @Nullable
+        String getHomepageUrl();
+    }
+
+    /**
+     * This instance must not be used after calling destroy().
+     */
+    void destroy();
+
+    /**
+     * Sets the recipient for events from {@link MostVisitedSites}. The observer may be notified
+     * synchronously or asynchronously.
+     * @param observer The observer to be notified.
+     * @param numSites The maximum number of sites to return.
+     */
+    void setObserver(Observer observer, int numSites);
+
+    /**
+     * Blacklists a URL from the most visited URLs list.
+     */
+    void addBlacklistedUrl(String url);
+
+    /**
+     * Removes a URL from the most visited URLs blacklist.
+     */
+    void removeBlacklistedUrl(String url);
+
+    /**
+     * Records metrics about an impression of the surface with tiles.
+     * @param tilesCount Count of tiles available on the surface at the moment.
+     */
+    void recordPageImpression(int tilesCount);
+
+    /**
+     * Records metrics about an impression of a tile including its source (local, server, ...) and
+     * its visual type.
+     * @param tile Object holding the details of a tile.
+     */
+    void recordTileImpression(Tile tile);
+
+    /**
+     * Records the opening of a Most Visited Item.
+     * @param tile Object holding the details of a tile.
+     */
+    void recordOpenedMostVisitedItem(Tile tile);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
new file mode 100644
index 0000000..5276129
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
@@ -0,0 +1,197 @@
+// 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.
+
+package org.chromium.chrome.browser.suggestions.mostvisited;
+
+import android.text.TextUtils;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNIAdditionalImport;
+import org.chromium.chrome.browser.ntp.NewTabPage;
+import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.suggestions.tile.Tile;
+import org.chromium.chrome.browser.util.FeatureUtilities;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Methods to bridge into native history to provide most recent urls, titles and thumbnails.
+ */
+@JNIAdditionalImport(MostVisitedSites.class) // Needed for the Observer usage in the native calls.
+public class MostVisitedSitesBridge
+        implements MostVisitedSites, HomepageManager.HomepageStateListener {
+    /**
+     * Maximum number of tiles that is explicitly supported. UMA relies on this value, so even if
+     * the UI supports it, getting more can raise unexpected issues.
+     */
+    public static final int MAX_TILE_COUNT = 12;
+
+    private long mNativeMostVisitedSitesBridge;
+
+    private MostVisitedSites.Observer mWrappedObserver;
+
+    /**
+     * MostVisitedSites constructor requires a valid user profile object.
+     *
+     * @param profile The profile for which to fetch most visited sites.
+     */
+    public MostVisitedSitesBridge(Profile profile) {
+        mNativeMostVisitedSitesBridge = nativeInit(profile);
+        // The first tile replaces is replaced with homepage tile if NTPButton is enabled. Setting
+        // a homepage client to provide Java side information.
+        if (FeatureUtilities.isNewTabPageButtonEnabled()
+                && FeatureUtilities.isHomepageTileEnabled()) {
+            nativeSetHomepageClient(mNativeMostVisitedSitesBridge, new HomepageClient() {
+                @Override
+                public boolean isHomepageTileEnabled() {
+                    return HomepageManager.isHomepageEnabled()
+                            && !NewTabPage.isNTPUrl(getHomepageUrl())
+                            && !TextUtils.isEmpty(HomepageManager.getHomepageUri());
+                }
+
+                @Override
+                public String getHomepageUrl() {
+                    return HomepageManager.getHomepageUri();
+                }
+            });
+            HomepageManager.getInstance().addListener(this);
+        }
+    }
+
+    /**
+     * Cleans up the C++ side of this class. This instance must not be used after calling destroy().
+     */
+    @Override
+    public void destroy() {
+        // Stop listening even if it was not started in the first place. (Handled without errors.)
+        HomepageManager.getInstance().removeListener(this);
+        assert mNativeMostVisitedSitesBridge != 0;
+        nativeDestroy(mNativeMostVisitedSitesBridge);
+        mNativeMostVisitedSitesBridge = 0;
+    }
+
+    @Override
+    public void setObserver(Observer observer, int numSites) {
+        assert numSites <= MAX_TILE_COUNT;
+        mWrappedObserver = observer;
+
+        nativeSetObserver(mNativeMostVisitedSitesBridge, this, numSites);
+    }
+
+    @Override
+    public void addBlacklistedUrl(String url) {
+        nativeAddOrRemoveBlacklistedUrl(mNativeMostVisitedSitesBridge, url, true);
+    }
+
+    @Override
+    public void removeBlacklistedUrl(String url) {
+        nativeAddOrRemoveBlacklistedUrl(mNativeMostVisitedSitesBridge, url, false);
+    }
+
+    @Override
+    public void recordPageImpression(int tilesCount) {
+        nativeRecordPageImpression(mNativeMostVisitedSitesBridge, tilesCount);
+    }
+
+    @Override
+    public void recordTileImpression(Tile tile) {
+        nativeRecordTileImpression(mNativeMostVisitedSitesBridge, tile.getIndex(), tile.getType(),
+                tile.getIconType(), tile.getTitleSource(), tile.getSource(),
+                tile.getData().dataGenerationTime.getTime(), tile.getUrl());
+    }
+
+    @Override
+    public void recordOpenedMostVisitedItem(Tile tile) {
+        nativeRecordOpenedMostVisitedItem(mNativeMostVisitedSitesBridge, tile.getIndex(),
+                tile.getType(), tile.getTitleSource(), tile.getSource(),
+                tile.getData().dataGenerationTime.getTime());
+    }
+
+    @Override
+    public void onHomepageStateUpdated() {
+        assert mNativeMostVisitedSitesBridge != 0;
+        // Ensure even a blacklisted homepage can be set as tile when (re-)enabling it.
+        if (HomepageManager.isHomepageEnabled()) {
+            removeBlacklistedUrl(HomepageManager.getHomepageUri());
+        }
+        nativeOnHomepageStateChanged(mNativeMostVisitedSitesBridge);
+    }
+
+    /**
+     * Utility function to convert JNI friendly site suggestion data to a Java friendly list of
+     * {@link SiteSuggestion}s.
+     */
+    public static List<SiteSuggestion> buildSiteSuggestions(String[] titles, String[] urls,
+            int[] sections, String[] whitelistIconPaths, int[] titleSources, int[] sources,
+            long[] dataGenerationTimesMs) {
+        List<SiteSuggestion> siteSuggestions = new ArrayList<>(titles.length);
+        for (int i = 0; i < titles.length; ++i) {
+            siteSuggestions.add(new SiteSuggestion(titles[i], urls[i], whitelistIconPaths[i],
+                    titleSources[i], sources[i], sections[i], new Date(dataGenerationTimesMs[i])));
+        }
+        return siteSuggestions;
+    }
+
+    /**
+     * This is called when the list of most visited URLs is initially available or updated.
+     * Parameters guaranteed to be non-null.
+     *
+     * @param titles Array of most visited url page titles.
+     * @param urls Array of most visited URLs, including popular URLs if
+     *             available and necessary (i.e. there aren't enough most
+     *             visited URLs).
+     * @param whitelistIconPaths The paths to the icon image files for whitelisted tiles, empty
+     *                           strings otherwise.
+     * @param sources For each tile, the {@code TileSource} that generated the tile.
+     */
+    @CalledByNative
+    private void onURLsAvailable(String[] titles, String[] urls, int[] sections,
+            String[] whitelistIconPaths, int[] titleSources, int[] sources,
+            long[] dataGenerationTimesMs) {
+        // Don't notify observer if we've already been destroyed.
+        if (mNativeMostVisitedSitesBridge == 0) return;
+
+        List<SiteSuggestion> suggestions = new ArrayList<>();
+
+        suggestions.addAll(buildSiteSuggestions(titles, urls, sections, whitelistIconPaths,
+                titleSources, sources, dataGenerationTimesMs));
+
+        mWrappedObserver.onSiteSuggestionsAvailable(suggestions);
+    }
+
+    /**
+     * This is called when a previously uncached icon has been fetched.
+     * Parameters guaranteed to be non-null.
+     *
+     * @param siteUrl URL of site with newly-cached icon.
+     */
+    @CalledByNative
+    private void onIconMadeAvailable(String siteUrl) {
+        // Don't notify observer if we've already been destroyed.
+        if (mNativeMostVisitedSitesBridge != 0) {
+            mWrappedObserver.onIconMadeAvailable(siteUrl);
+        }
+    }
+
+    private native long nativeInit(Profile profile);
+    private native void nativeDestroy(long nativeMostVisitedSitesBridge);
+    private native void nativeOnHomepageStateChanged(long nativeMostVisitedSitesBridge);
+    private native void nativeSetHomepageClient(
+            long nativeMostVisitedSitesBridge, MostVisitedSites.HomepageClient homePageClient);
+    private native void nativeSetObserver(
+            long nativeMostVisitedSitesBridge, MostVisitedSitesBridge observer, int numSites);
+    private native void nativeAddOrRemoveBlacklistedUrl(
+            long nativeMostVisitedSitesBridge, String url, boolean addUrl);
+    private native void nativeRecordPageImpression(
+            long nativeMostVisitedSitesBridge, int tilesCount);
+    private native void nativeRecordTileImpression(long nativeMostVisitedSitesBridge, int index,
+            int type, int iconType, int titleSource, int source, long dataGenerationTimeMs,
+            String url);
+    private native void nativeRecordOpenedMostVisitedItem(long nativeMostVisitedSitesBridge,
+            int index, int tileType, int titleSource, int source, long dataGenerationTimeMs);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/OWNERS
new file mode 100644
index 0000000..125bc14
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/android/explore_sites/OWNERS
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java
new file mode 100644
index 0000000..467575cf
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java
@@ -0,0 +1,125 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.support.annotation.LayoutRes;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
+import org.chromium.chrome.browser.native_page.ContextMenuManager;
+import org.chromium.chrome.browser.ntp.cards.ItemViewType;
+import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
+import org.chromium.chrome.browser.ntp.cards.OptionalLeaf;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.suggestions.SuggestionsConfig;
+import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
+import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+
+/**
+ * The model and controller for a group of site suggestions.
+ * @deprecated This class is still being used, but not in the New Tab Page RecyclerView
+ *         anymore. It still uses the latter's base classes until SiteSection is migrated to the new
+ *         UI architecture.
+ */
+@Deprecated
+public class SiteSection extends OptionalLeaf implements TileGroup.Observer {
+    /**
+     * The maximum number of tiles to try and fit in a row. On smaller screens, there may not be
+     * enough space to fit all of them.
+     */
+    private static final int MAX_TILE_COLUMNS = 4;
+    private static final int TILE_TITLE_LINES = 1;
+
+    private final TileGroup mTileGroup;
+    private final TileRenderer mTileRenderer;
+
+    public static ViewGroup inflateSiteSection(ViewGroup parent) {
+        return (ViewGroup) LayoutInflater.from(parent.getContext())
+                .inflate(getLayout(), parent, false);
+    }
+
+    public static SiteSectionViewHolder createViewHolder(ViewGroup view, UiConfig uiConfig) {
+        return new TileGridViewHolder(view, getMaxTileRows(), MAX_TILE_COLUMNS);
+    }
+
+    public SiteSection(SuggestionsUiDelegate uiDelegate, ContextMenuManager contextMenuManager,
+            TileGroup.Delegate tileGroupDelegate, OfflinePageBridge offlinePageBridge,
+            UiConfig uiConfig) {
+        mTileRenderer = new TileRenderer(ContextUtils.getApplicationContext(),
+                SuggestionsConfig.getTileStyle(uiConfig), TILE_TITLE_LINES,
+                uiDelegate.getImageFetcher());
+        mTileGroup = new TileGroup(mTileRenderer, uiDelegate, contextMenuManager, tileGroupDelegate,
+                /* observer = */ this, offlinePageBridge);
+        mTileGroup.startObserving(MAX_TILE_COLUMNS * getMaxTileRows());
+    }
+
+    @Override
+    @ItemViewType
+    protected int getItemViewType() {
+        // Throw an exception instead of just `assert false` to avoid compiler warnings about the
+        // return value.
+        throw new IllegalStateException();
+    }
+
+    @Override
+    protected void onBindViewHolder(NewTabPageViewHolder holder) {
+        SiteSectionViewHolder siteSectionView = (SiteSectionViewHolder) holder;
+        siteSectionView.bindDataSource(mTileGroup, mTileRenderer);
+        siteSectionView.refreshData();
+    }
+
+    @Override
+    public String describeForTesting() {
+        // Throw an exception instead of just `assert false` to avoid compiler warnings about the
+        // return value.
+        throw new IllegalStateException();
+    }
+
+    @Override
+    public void onTileDataChanged() {
+        setVisibilityInternal(!mTileGroup.isEmpty());
+        if (!isVisible()) return;
+        notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).refreshData());
+    }
+
+    @Override
+    public void onTileCountChanged() {
+        onTileDataChanged();
+    }
+
+    @Override
+    public void onTileIconChanged(Tile tile) {
+        if (!isVisible()) return;
+        notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).updateIconView(tile));
+    }
+
+    @Override
+    public void onTileOfflineBadgeVisibilityChanged(Tile tile) {
+        if (!isVisible()) return;
+        notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).updateOfflineBadge(tile));
+    }
+
+    TileGroup getTileGroupForTesting() {
+        return mTileGroup;
+    }
+
+    private static int getMaxTileRows() {
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.EXPLORE_SITES)
+                && !ExploreSitesBridge.isIntegratedWithMostLikely(
+                        ExploreSitesBridge.getVariation())) {
+            return 1;
+        }
+        return 2;
+    }
+
+    @LayoutRes
+    private static int getLayout() {
+        return R.layout.suggestions_site_tile_grid_modern;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSectionViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSectionViewHolder.java
new file mode 100644
index 0000000..63c31d9e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSectionViewHolder.java
@@ -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.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.support.annotation.CallSuper;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+
+/**
+ * Describes a portion of UI responsible for rendering a group of sites. It abstracts general tasks
+ * related to initialising and updating this UI.
+ */
+public abstract class SiteSectionViewHolder extends NewTabPageViewHolder {
+    protected TileGroup mTileGroup;
+    protected TileRenderer mTileRenderer;
+
+    /**
+     * Constructs a {@link SiteSectionViewHolder} used to display tiles in both NTP and Chrome Home.
+     *
+     * @param itemView The {@link View} for this item
+     */
+    public SiteSectionViewHolder(View itemView) {
+        super(itemView);
+    }
+
+    /** Initialise the view, letting it know the data it will have to display. */
+    @CallSuper
+    public void bindDataSource(TileGroup tileGroup, TileRenderer tileRenderer) {
+        mTileGroup = tileGroup;
+        mTileRenderer = tileRenderer;
+    }
+
+    /**
+     * Sets a new icon on the child view with a matching site.
+     * @param tile The tile that holds the data to populate the tile view.
+     */
+    public void updateIconView(Tile tile) {
+        SuggestionsTileView tileView = findTileView(tile.getData());
+        if (tileView != null) tileView.renderIcon(tile);
+    }
+
+    /**
+     * Updates the visibility of the offline badge on the child view with a matching site.
+     * @param tile The tile that holds the data to populate the tile view.
+     */
+    public void updateOfflineBadge(Tile tile) {
+        SuggestionsTileView tileView = findTileView(tile.getData());
+        if (tileView != null) tileView.renderOfflineBadge(tile);
+    }
+
+    /** Clears the current data and displays the current state of the model. */
+    public abstract void refreshData();
+
+    @Nullable
+    public abstract SuggestionsTileView findTileView(SiteSuggestion data);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java
new file mode 100644
index 0000000..a00bb6e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java
@@ -0,0 +1,81 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ntp.TitleUtil;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.widget.tile.TileWithTextView;
+
+/**
+ * The view for a site suggestion tile. Displays the title of the site beneath a large icon. If a
+ * large icon isn't available, displays a rounded rectangle with a single letter in its place.
+ */
+public class SuggestionsTileView extends TileWithTextView {
+    /** The data currently associated to this tile. */
+    private SiteSuggestion mData;
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public SuggestionsTileView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * Initializes the view using the data held by {@code tile}. This should be called immediately
+     * after inflation.
+     * @param tile The tile that holds the data to populate this view.
+     * @param titleLines The number of text lines to use for the tile title.
+     */
+    public void initialize(Tile tile, int titleLines) {
+        super.initialize(TitleUtil.getTitleForDisplay(tile.getTitle(), tile.getUrl()),
+                tile.isOfflineAvailable(), tile.getIcon(), titleLines);
+        mData = tile.getData();
+        setIconViewLayoutParams(tile);
+    }
+
+    /** Retrieves data associated with this view.  */
+    public SiteSuggestion getData() {
+        return mData;
+    }
+
+    /** Retrieves url associated with this view. */
+    public String getUrl() {
+        return mData.url;
+    }
+
+    /** Renders icon based on tile data.  */
+    public void renderIcon(Tile tile) {
+        setIconDrawable(tile.getIcon());
+        setIconViewLayoutParams(tile);
+    }
+
+    public void renderOfflineBadge(Tile tile) {
+        setOfflineBadgeVisibility(tile.isOfflineAvailable());
+    }
+
+    protected void setIconViewLayoutParams(Tile tile) {
+        MarginLayoutParams params = (MarginLayoutParams) mIconView.getLayoutParams();
+        Resources resources = getResources();
+        if (tile.getType() == TileVisualType.ICON_COLOR
+                || tile.getType() == TileVisualType.ICON_DEFAULT) {
+            params.width = resources.getDimensionPixelSize(R.dimen.tile_view_monogram_size_modern);
+            params.height = resources.getDimensionPixelSize(R.dimen.tile_view_monogram_size_modern);
+            params.topMargin =
+                    resources.getDimensionPixelSize(R.dimen.tile_view_monogram_margin_top_modern);
+        } else {
+            params.width = resources.getDimensionPixelSize(R.dimen.tile_view_icon_size_modern);
+            params.height = resources.getDimensionPixelSize(R.dimen.tile_view_icon_size_modern);
+            params.topMargin =
+                    resources.getDimensionPixelSize(R.dimen.tile_view_icon_margin_top_modern);
+        }
+        mIconView.setLayoutParams(params);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java
new file mode 100644
index 0000000..4cff7eb3
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java
@@ -0,0 +1,157 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+
+import org.chromium.chrome.browser.favicon.IconType;
+import org.chromium.chrome.browser.suggestions.OfflinableSuggestion;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+
+/**
+ * Holds the details to populate a site suggestion tile.
+ */
+public class Tile implements OfflinableSuggestion {
+    private final SiteSuggestion mSiteData;
+
+    private final int mIndex;
+
+    @TileVisualType
+    private int mType = TileVisualType.NONE;
+
+    @IconType
+    private int mIconType = IconType.INVALID;
+
+    @Nullable
+    private Drawable mIcon;
+
+    @Nullable
+    private Long mOfflinePageOfflineId;
+
+    /**
+     * @param suggestion The site data we want to populate the tile with.
+     * @param index The index of this tile in the list of tiles.
+     */
+    public Tile(SiteSuggestion suggestion, int index) {
+        mSiteData = suggestion;
+        mIndex = index;
+    }
+
+    public SiteSuggestion getData() {
+        return mSiteData;
+    }
+
+    @Override
+    public String getUrl() {
+        return mSiteData.url;
+    }
+
+    @Override
+    public void setOfflinePageOfflineId(@Nullable Long offlineId) {
+        mOfflinePageOfflineId = offlineId;
+    }
+
+    @Nullable
+    @Override
+    public Long getOfflinePageOfflineId() {
+        return mOfflinePageOfflineId;
+    }
+
+    @Override
+    public boolean requiresExactOfflinePage() {
+        return false;
+    }
+
+    /**
+     * @return The title of this tile.
+     */
+    public String getTitle() {
+        return mSiteData.title;
+    }
+
+    /**
+     * @return Whether this tile is available offline.
+     */
+    public boolean isOfflineAvailable() {
+        return getOfflinePageOfflineId() != null;
+    }
+
+    /**
+     * @return The index of this tile in the list of tiles.
+     */
+    public int getIndex() {
+        return mIndex;
+    }
+
+    /**
+     * @return The source of this tile's title. Used for metrics tracking. Valid values are listed
+     * in {@code TileTitleSource}.
+     */
+    @TileTitleSource
+    public int getTitleSource() {
+        return mSiteData.titleSource;
+    }
+
+    /**
+     * @return The source of this tile. Used for metrics tracking. Valid values are listed in
+     * {@code TileSource}.
+     */
+    @TileSource
+    public int getSource() {
+        return mSiteData.source;
+    }
+
+    /**
+     * @return The visual type of this tile. Valid values are listed in {@link TileVisualType}.
+     */
+    @TileVisualType
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Sets the visual type of this tile. Valid values are listed in
+     * {@link TileVisualType}.
+     */
+    public void setType(@TileVisualType int type) {
+        mType = type;
+    }
+
+    /**
+     * @return The icon type of this tile. Valid values are listed in {@link IconType}.
+     */
+    @IconType
+    public int getIconType() {
+        return mIconType;
+    }
+
+    /**
+     * Sets the icon type of this tile. Valid values are listed in {@link IconType}.
+     */
+    public void setIconType(@IconType int iconType) {
+        mIconType = iconType;
+    }
+
+    /**
+     * @return The icon, may be null.
+     */
+    @Nullable
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Updates the icon drawable.
+     */
+    public void setIcon(@Nullable Drawable icon) {
+        mIcon = icon;
+    }
+
+    @TileSectionType
+    public int getSectionType() {
+        return mSiteData.sectionType;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayout.java
new file mode 100644
index 0000000..462d50eb
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayout.java
@@ -0,0 +1,176 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.util.MathUtils;
+
+/**
+ * A layout that arranges tiles in a grid.
+ */
+public class TileGridLayout extends FrameLayout {
+    private final int mVerticalSpacing;
+    private final int mMinHorizontalSpacing;
+    private final int mMaxHorizontalSpacing;
+    private final int mMaxWidth;
+
+    private int mMaxRows;
+    private int mMaxColumns;
+
+    /**
+     * Constructor for inflating from XML.
+     *
+     * @param context The view context in which this item will be shown.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     */
+    public TileGridLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        Resources res = getResources();
+        mVerticalSpacing = res.getDimensionPixelOffset(R.dimen.tile_grid_layout_vertical_spacing);
+        TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.TileGridLayout);
+        mMinHorizontalSpacing = styledAttrs.getDimensionPixelOffset(
+                R.styleable.TileGridLayout_minHorizontalSpacing,
+                res.getDimensionPixelOffset(R.dimen.tile_grid_layout_min_horizontal_spacing));
+        styledAttrs.recycle();
+        mMaxHorizontalSpacing = Integer.MAX_VALUE;
+        mMaxWidth = Integer.MAX_VALUE;
+    }
+
+    /**
+     * Sets the maximum number of rows to display. Any items that don't fit will be hidden.
+     */
+    public void setMaxRows(int rows) {
+        mMaxRows = rows;
+    }
+
+    /**
+     * Sets the maximum number of columns to display. Any items that don't fit will be hidden.
+     */
+    public void setMaxColumns(int columns) {
+        mMaxColumns = columns;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int totalWidth = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
+        int childCount = getChildCount();
+        if (childCount == 0) {
+            setMeasuredDimension(totalWidth, resolveSize(0, heightMeasureSpec));
+            return;
+        }
+
+        // Measure the children. We don't use the ViewGroup.measureChildren() method here because
+        // it only measures visible children. In a situation where a child is invisible before
+        // this measurement and we decide to show it after the measurement, it will not have its
+        // dimensions and will not be displayed.
+        for (int i = 0; i < childCount; i++) {
+            measureChild(getChildAt(i), MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        }
+
+        // Determine the number of columns that will fit.
+        int childHeight = getChildAt(0).getMeasuredHeight();
+        int childWidth = getChildAt(0).getMeasuredWidth();
+        int numColumns = MathUtils.clamp(
+                (totalWidth + mMinHorizontalSpacing) / (childWidth + mMinHorizontalSpacing), 1,
+                mMaxColumns);
+
+        // Determine how much padding to use between and around the tiles.
+        int gridWidthMinusColumns = Math.max(0, totalWidth - numColumns * childWidth);
+        Pair<Integer, Integer> gridProperties =
+                computeHorizontalDimensions(true, gridWidthMinusColumns, numColumns);
+        int gridStart = gridProperties.first;
+        int horizontalSpacing = gridProperties.second;
+
+        // Limit the number of rows to mMaxRows.
+        int visibleChildCount = Math.min(childCount, mMaxRows * numColumns);
+
+        // Arrange the visible children in a grid.
+        int numRows = (visibleChildCount + numColumns - 1) / numColumns;
+        int paddingTop = getPaddingTop();
+        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+
+        for (int i = 0; i < visibleChildCount; i++) {
+            View child = getChildAt(i);
+            child.setVisibility(View.VISIBLE);
+            int row = i / numColumns;
+            int column = i % numColumns;
+            int childTop = row * (childHeight + mVerticalSpacing);
+            int childStart = gridStart + (column * (childWidth + horizontalSpacing));
+            MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
+            layoutParams.setMargins(isRtl ? 0 : childStart, childTop, isRtl ? childStart : 0, 0);
+            child.setLayoutParams(layoutParams);
+        }
+
+        // Hide any extra children in case there are more than needed for the maximum number of
+        // rows.
+        for (int i = visibleChildCount; i < childCount; i++) {
+            getChildAt(i).setVisibility(View.GONE);
+        }
+
+        int totalHeight = paddingTop + getPaddingBottom() + numRows * childHeight
+                + (numRows - 1) * mVerticalSpacing;
+
+        setMeasuredDimension(totalWidth, resolveSize(totalHeight, heightMeasureSpec));
+    }
+
+    /**
+     * @param spreadTiles Whether to spread the tiles with the same space between and around them.
+     * @param availableWidth The space available to spread between and around the tiles.
+     * @param numColumns The number of columns to be organised.
+     * @return The [gridStart, horizontalSpacing] pair of dimensions.
+     */
+    @VisibleForTesting
+    Pair<Integer, Integer> computeHorizontalDimensions(
+            boolean spreadTiles, int availableWidth, int numColumns) {
+        int gridStart;
+        float horizontalSpacing;
+        if (spreadTiles) {
+            // Identically sized spacers are added both between and around the tiles.
+            int spacerCount = numColumns + 1;
+            horizontalSpacing = (float) availableWidth / spacerCount;
+            gridStart = Math.round(horizontalSpacing);
+            if (horizontalSpacing < mMinHorizontalSpacing) {
+                return computeHorizontalDimensions(false, availableWidth, numColumns);
+            }
+        } else {
+            // Ensure column spacing isn't greater than mMaxHorizontalSpacing.
+            long gridSidePadding = availableWidth - (long) mMaxHorizontalSpacing * (numColumns - 1);
+            if (gridSidePadding > 0) {
+                horizontalSpacing = mMaxHorizontalSpacing;
+                gridStart = (int) (gridSidePadding / 2);
+            } else {
+                horizontalSpacing = (float) availableWidth / Math.max(1, numColumns - 1);
+                gridStart = 0;
+            }
+        }
+
+        assert horizontalSpacing >= mMinHorizontalSpacing;
+        assert horizontalSpacing <= mMaxHorizontalSpacing;
+
+        return Pair.create(gridStart, Math.round(horizontalSpacing));
+    }
+
+    @Nullable
+    public SuggestionsTileView getTileView(SiteSuggestion suggestion) {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            SuggestionsTileView tileView = (SuggestionsTileView) getChildAt(i);
+            if (suggestion.equals(tileView.getData())) return tileView;
+        }
+        return null;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridViewHolder.java
new file mode 100644
index 0000000..b094260b
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridViewHolder.java
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.view.ViewGroup;
+
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+
+import java.util.List;
+/**
+ * A {@link SiteSectionViewHolder} specialised in displaying sites as a simple grid of tiles,
+ * through
+ * {@link TileGridLayout}.
+ */
+public class TileGridViewHolder extends SiteSectionViewHolder {
+    private final TileGridLayout mSectionView;
+
+    public TileGridViewHolder(ViewGroup view, int maxRows, int maxColumns) {
+        super(view);
+
+        mSectionView = (TileGridLayout) itemView;
+        mSectionView.setMaxRows(maxRows);
+        mSectionView.setMaxColumns(maxColumns);
+    }
+
+    @Override
+    public void refreshData() {
+        assert mTileGroup.getTileSections().size() == 1;
+        List<Tile> tiles = mTileGroup.getTileSections().get(TileSectionType.PERSONALIZED);
+        assert tiles != null;
+
+        mTileRenderer.renderTileSection(tiles, mSectionView, mTileGroup.getTileSetupDelegate());
+        mTileGroup.notifyTilesRendered();
+    }
+
+    @Override
+    public SuggestionsTileView findTileView(SiteSuggestion data) {
+        return mSectionView.getTileView(data);
+    }
+
+    @Override
+    public void bindDataSource(TileGroup tileGroup, TileRenderer tileRenderer) {
+        super.bindDataSource(tileGroup, tileRenderer);
+    }
+
+    @Override
+    public void recycle() {
+        super.recycle();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
new file mode 100644
index 0000000..4d0da06
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -0,0 +1,613 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.graphics.Bitmap;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.util.SparseArray;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnCreateContextMenuListener;
+
+import org.chromium.base.Callback;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.favicon.IconType;
+import org.chromium.chrome.browser.favicon.LargeIconBridge;
+import org.chromium.chrome.browser.native_page.ContextMenuManager;
+import org.chromium.chrome.browser.native_page.ContextMenuManager.ContextMenuItemId;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.suggestions.SuggestionsConfig;
+import org.chromium.chrome.browser.suggestions.SuggestionsMetrics;
+import org.chromium.chrome.browser.suggestions.SuggestionsOfflineModelObserver;
+import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites;
+import org.chromium.ui.mojom.WindowOpenDisposition;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * The model and controller for a group of site suggestion tiles.
+ */
+public class TileGroup implements MostVisitedSites.Observer {
+    /**
+     * Performs work in other parts of the system that the {@link TileGroup} should not know about.
+     */
+    public interface Delegate {
+        /**
+         * @param tile The tile corresponding to the most visited item to remove.
+         * @param removalUndoneCallback The callback to invoke if the removal is reverted. The
+         *                              callback's argument is the URL being restored.
+         */
+        void removeMostVisitedItem(Tile tile, Callback<String> removalUndoneCallback);
+
+        void openMostVisitedItem(int windowDisposition, Tile tile);
+
+        /**
+         * Gets the list of most visited sites.
+         * @param observer The observer to be notified with the list of sites.
+         * @param maxResults The maximum number of sites to retrieve.
+         */
+        void setMostVisitedSitesObserver(MostVisitedSites.Observer observer, int maxResults);
+
+        /**
+         * Called when the tile group has completely finished loading (all views will be inflated
+         * and any dependent resources will have been loaded).
+         * @param tiles The tiles owned by the {@link TileGroup}. Used to record metrics.
+         */
+        void onLoadingComplete(List<Tile> tiles);
+
+        /**
+         * To be called before this instance is abandoned to the garbage collector so it can do any
+         * necessary cleanups. This instance must not be used after this method is called.
+         */
+        void destroy();
+    }
+
+    /**
+     * An observer for events in the {@link TileGroup}.
+     */
+    public interface Observer {
+        /**
+         * Called when the tile group is initialised and when any of the tile data has changed,
+         * such as an icon, url, or title.
+         */
+        void onTileDataChanged();
+
+        /**
+         * Called when the number of tiles has changed.
+         */
+        void onTileCountChanged();
+
+        /**
+         * Called when a tile icon has changed.
+         * @param tile The tile for which the icon has changed.
+         */
+        void onTileIconChanged(Tile tile);
+
+        /**
+         * Called when the visibility of a tile's offline badge has changed.
+         * @param tile The tile for which the visibility of the offline badge has changed.
+         */
+        void onTileOfflineBadgeVisibilityChanged(Tile tile);
+    }
+
+    /**
+     * A delegate to allow {@link TileRenderer} to setup behaviours for the newly created views
+     * associated to a Tile.
+     */
+    public interface TileSetupDelegate {
+        /**
+         * Returns a delegate that will handle user interactions with the view created for the tile.
+         */
+        TileInteractionDelegate createInteractionDelegate(Tile tile);
+
+        /**
+         * Returns a callback to be invoked when the icon for the provided tile is loaded. It will
+         * be responsible for updating the tile data and triggering the visual refresh.
+         */
+        LargeIconBridge.LargeIconCallback createIconLoadCallback(Tile tile);
+    }
+
+    /**
+     * Constants used to track the current operations on the group and notify the {@link Delegate}
+     * when the expected sequence of potentially asynchronous operations is complete.
+     */
+    @VisibleForTesting
+    @IntDef({TileTask.FETCH_DATA, TileTask.SCHEDULE_ICON_FETCH, TileTask.FETCH_ICON})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface TileTask {
+        /**
+         * An event that should result in new data being loaded happened.
+         * Can be an asynchronous task, spanning from when the {@link Observer} is registered to
+         * when the initial load completes.
+         */
+        int FETCH_DATA = 1;
+
+        /**
+         * New tile data has been loaded and we are expecting the related icons to be fetched.
+         * Can be an asynchronous task, as we rely on it being triggered by the embedder, some time
+         * after {@link Observer#onTileDataChanged()} is called.
+         */
+        int SCHEDULE_ICON_FETCH = 2;
+
+        /**
+         * The icon for a tile is being fetched.
+         * Asynchronous task, that is started for each icon that needs to be loaded.
+         */
+        int FETCH_ICON = 3;
+    }
+
+    private final SuggestionsUiDelegate mUiDelegate;
+    private final ContextMenuManager mContextMenuManager;
+    private final Delegate mTileGroupDelegate;
+    private final Observer mObserver;
+    private final TileRenderer mTileRenderer;
+
+    /**
+     * Tracks the tasks currently in flight.
+     *
+     * We only care about which ones are pending, not their order, and we can have multiple tasks
+     * pending of the same type. Hence exposing the type as Collection rather than List or Set.
+     */
+    private final Collection<Integer> mPendingTasks = new ArrayList<>();
+
+    /** Access point to offline related features. */
+    private final OfflineModelObserver mOfflineModelObserver;
+
+    /**
+     * Source of truth for the tile data. Avoid keeping a reference to a tile in long running
+     * callbacks, as it might be thrown out before it is called. Use URL or site data to look it up
+     * at the right time instead.
+     * @see #findTile(SiteSuggestion)
+     * @see #findTilesForUrl(String)
+     */
+    private SparseArray<List<Tile>> mTileSections = createEmptyTileData();
+
+    /** Most recently received tile data that has not been displayed yet. */
+    @Nullable
+    private List<SiteSuggestion> mPendingTiles;
+
+    /**
+     * URL of the most recently removed tile. Used to identify when a tile removal is confirmed by
+     * the tile backend.
+     */
+    @Nullable
+    private String mPendingRemovalUrl;
+
+    /**
+     * URL of the most recently added tile. Used to identify when a given tile's insertion is
+     * confirmed by the tile backend. This is relevant when a previously existing tile is removed,
+     * then the user undoes the action and wants that tile back.
+     */
+    @Nullable
+    private String mPendingInsertionUrl;
+
+    private boolean mHasReceivedData;
+
+    // TODO(dgn): Attempt to avoid cycling dependencies with TileRenderer. Is there a better way?
+    private final TileSetupDelegate mTileSetupDelegate = new TileSetupDelegate() {
+        @Override
+        public TileInteractionDelegate createInteractionDelegate(Tile tile) {
+            return new TileInteractionDelegate(tile.getData());
+        }
+
+        @Override
+        public LargeIconBridge.LargeIconCallback createIconLoadCallback(Tile tile) {
+            // TODO(dgn): We could save on fetches by avoiding a new one when there is one pending
+            // for the same URL, and applying the result to all matched URLs.
+            boolean trackLoad =
+                    isLoadTracked() && tile.getSectionType() == TileSectionType.PERSONALIZED;
+            if (trackLoad) addTask(TileTask.FETCH_ICON);
+            return new LargeIconCallbackImpl(tile.getData(), trackLoad);
+        }
+    };
+
+    /**
+     * @param tileRenderer Used to render icons.
+     * @param uiDelegate Delegate used to interact with the rest of the system.
+     * @param contextMenuManager Used to handle context menu invocations on the tiles.
+     * @param tileGroupDelegate Used for interactions with the Most Visited backend.
+     * @param observer Will be notified of changes to the tile data.
+     * @param offlinePageBridge Used to update the offline badge of the tiles.
+     */
+    public TileGroup(TileRenderer tileRenderer, SuggestionsUiDelegate uiDelegate,
+            ContextMenuManager contextMenuManager, Delegate tileGroupDelegate, Observer observer,
+            OfflinePageBridge offlinePageBridge) {
+        mUiDelegate = uiDelegate;
+        mContextMenuManager = contextMenuManager;
+        mTileGroupDelegate = tileGroupDelegate;
+        mObserver = observer;
+        mTileRenderer = tileRenderer;
+        mOfflineModelObserver = new OfflineModelObserver(offlinePageBridge);
+        mUiDelegate.addDestructionObserver(mOfflineModelObserver);
+    }
+
+    @Override
+    public void onSiteSuggestionsAvailable(List<SiteSuggestion> siteSuggestions) {
+        // Only transforms the incoming tiles and stores them in a buffer for when we decide to
+        // refresh the tiles in the UI.
+
+        boolean removalCompleted = mPendingRemovalUrl != null;
+        boolean insertionCompleted = mPendingInsertionUrl == null;
+
+        mPendingTiles = new ArrayList<>();
+        for (SiteSuggestion suggestion : siteSuggestions) {
+            mPendingTiles.add(suggestion);
+
+            // Only tiles in the personal section can be modified.
+            if (suggestion.sectionType != TileSectionType.PERSONALIZED) continue;
+            if (suggestion.url.equals(mPendingRemovalUrl)) removalCompleted = false;
+            if (suggestion.url.equals(mPendingInsertionUrl)) insertionCompleted = true;
+        }
+
+        boolean expectedChangeCompleted = false;
+        if (mPendingRemovalUrl != null && removalCompleted) {
+            mPendingRemovalUrl = null;
+            expectedChangeCompleted = true;
+        }
+        if (mPendingInsertionUrl != null && insertionCompleted) {
+            mPendingInsertionUrl = null;
+            expectedChangeCompleted = true;
+        }
+
+        if (!mHasReceivedData || !mUiDelegate.isVisible() || expectedChangeCompleted) loadTiles();
+    }
+
+    @Override
+    public void onIconMadeAvailable(String siteUrl) {
+        for (Tile tile : findTilesForUrl(siteUrl)) {
+            mTileRenderer.updateIcon(tile.getData(),
+                    new LargeIconCallbackImpl(tile.getData(), /* trackLoadTask = */ false));
+        }
+    }
+
+    /**
+     * Instructs this instance to start listening for data. The {@link TileGroup.Observer} may be
+     * called immediately if new data is received synchronously.
+     * @param maxResults The maximum number of sites to retrieve.
+     */
+    public void startObserving(int maxResults) {
+        addTask(TileTask.FETCH_DATA);
+        mTileGroupDelegate.setMostVisitedSitesObserver(this, maxResults);
+    }
+
+    /**
+     * Method to be called when a tile render has been triggered, to let the {@link TileGroup}
+     * update its internal task tracking status.
+     * @see Delegate#onLoadingComplete(List)
+     */
+    public void notifyTilesRendered() {
+        // Icon fetch scheduling was done when building the tile views.
+        if (isLoadTracked()) removeTask(TileTask.SCHEDULE_ICON_FETCH);
+    }
+
+    /** @return the sites currently loaded in the group, grouped by vertical. */
+    public SparseArray<List<Tile>> getTileSections() {
+        return mTileSections;
+    }
+
+    public boolean hasReceivedData() {
+        return mHasReceivedData;
+    }
+
+    /** @return Whether the group has no sites to display. */
+    public boolean isEmpty() {
+        for (int i = 0; i < mTileSections.size(); i++) {
+            if (!mTileSections.valueAt(i).isEmpty()) return false;
+        }
+        return true;
+    }
+
+    /**
+     * To be called when the view displaying the tile group becomes visible.
+     * @param trackLoadTask whether the delegate should be notified that the load is completed
+     *      through {@link Delegate#onLoadingComplete(List)}.
+     */
+    public void onSwitchToForeground(boolean trackLoadTask) {
+        if (trackLoadTask) addTask(TileTask.FETCH_DATA);
+        if (mPendingTiles != null) loadTiles();
+        if (trackLoadTask) removeTask(TileTask.FETCH_DATA);
+    }
+
+    /** Loads tile data from {@link #mPendingTiles} and clears it afterwards. */
+    private void loadTiles() {
+        assert mPendingTiles != null;
+
+        boolean isInitialLoad = !mHasReceivedData;
+        mHasReceivedData = true;
+
+        boolean dataChanged = isInitialLoad;
+        List<Tile> personalisedTiles = mTileSections.get(TileSectionType.PERSONALIZED);
+        int oldPersonalisedTilesCount = personalisedTiles == null ? 0 : personalisedTiles.size();
+
+        SparseArray<List<Tile>> newSites = createEmptyTileData();
+        for (int i = 0; i < mPendingTiles.size(); ++i) {
+            SiteSuggestion suggestion = mPendingTiles.get(i);
+            Tile tile = findTile(suggestion);
+            if (tile == null) {
+                dataChanged = true;
+                tile = new Tile(suggestion, i);
+            }
+
+            List<Tile> sectionTiles = newSites.get(suggestion.sectionType);
+            if (sectionTiles == null) {
+                sectionTiles = new ArrayList<>();
+                newSites.append(suggestion.sectionType, sectionTiles);
+            }
+
+            // This is not supposed to happen but does. See https://crbug.com/703628
+            if (findTile(suggestion.url, sectionTiles) != null) continue;
+
+            sectionTiles.add(tile);
+        }
+
+        mTileSections = newSites;
+        mPendingTiles = null;
+
+        // TODO(dgn): change these events, maybe introduce new ones or just change semantics? This
+        // will depend on the UI to be implemented and the desired refresh behaviour.
+        List<Tile> personalizedTiles = mTileSections.get(TileSectionType.PERSONALIZED);
+        int numberOfPersonalizedTiles = personalizedTiles == null ? 0 : personalizedTiles.size();
+        boolean countChanged =
+                isInitialLoad || numberOfPersonalizedTiles != oldPersonalisedTilesCount;
+        dataChanged = dataChanged || countChanged;
+
+        if (!dataChanged) return;
+
+        mOfflineModelObserver.updateAllSuggestionsOfflineAvailability(
+                /* reportPrefetchedSuggestionsCount = */ false);
+
+        if (countChanged) mObserver.onTileCountChanged();
+
+        if (isLoadTracked()) addTask(TileTask.SCHEDULE_ICON_FETCH);
+        mObserver.onTileDataChanged();
+
+        if (isInitialLoad) removeTask(TileTask.FETCH_DATA);
+    }
+
+    @Nullable
+    private Tile findTile(SiteSuggestion suggestion) {
+        if (mTileSections.get(suggestion.sectionType) == null) return null;
+        for (Tile tile : mTileSections.get(suggestion.sectionType)) {
+            if (tile.getData().equals(suggestion)) return tile;
+        }
+        return null;
+    }
+
+    /**
+     * @param url The URL to search for.
+     * @param tiles The section to search in, represented by the contained list of tiles.
+     * @return A tile matching the provided URL and section, or {@code null} if none is found.
+     */
+    private Tile findTile(String url, @Nullable List<Tile> tiles) {
+        if (tiles == null) return null;
+        for (Tile tile : tiles) {
+            if (tile.getUrl().equals(url)) return tile;
+        }
+        return null;
+    }
+
+    /** @return All tiles matching the provided URL, or an empty list if none is found. */
+    private List<Tile> findTilesForUrl(String url) {
+        List<Tile> tiles = new ArrayList<>();
+        for (int i = 0; i < mTileSections.size(); ++i) {
+            for (Tile tile : mTileSections.valueAt(i)) {
+                if (tile.getUrl().equals(url)) tiles.add(tile);
+            }
+        }
+        return tiles;
+    }
+
+    private void addTask(@TileTask int task) {
+        mPendingTasks.add(task);
+    }
+
+    private void removeTask(@TileTask int task) {
+        boolean removedTask = mPendingTasks.remove(task);
+        assert removedTask;
+
+        if (mPendingTasks.isEmpty()) {
+            // TODO(dgn): We only notify about the personal tiles because that's the only ones we
+            // wait for to be loaded. We also currently rely on the tile order in the returned
+            // array as the reported position in UMA, but this is not accurate and would be broken
+            // if we returned all the tiles regardless of sections.
+            List<Tile> personalTiles = mTileSections.get(TileSectionType.PERSONALIZED);
+            assert personalTiles != null;
+            mTileGroupDelegate.onLoadingComplete(personalTiles);
+        }
+    }
+
+    /**
+     * @return Whether the current load is being tracked. Unrequested task tracking updates should
+     * not be sent, as it would cause calling {@link Delegate#onLoadingComplete(List)} at the
+     * wrong moment.
+     */
+    private boolean isLoadTracked() {
+        return mPendingTasks.contains(TileTask.FETCH_DATA)
+                || mPendingTasks.contains(TileTask.SCHEDULE_ICON_FETCH);
+    }
+
+    @VisibleForTesting
+    boolean isTaskPending(@TileTask int task) {
+        return mPendingTasks.contains(task);
+    }
+
+    @VisibleForTesting
+    TileSetupDelegate getTileSetupDelegate() {
+        return mTileSetupDelegate;
+    }
+
+    @Nullable
+    public SiteSuggestion getHomepageTileData() {
+        for (Tile tile : mTileSections.get(TileSectionType.PERSONALIZED)) {
+            if (tile.getSource() == TileSource.HOMEPAGE) {
+                return tile.getData();
+            }
+        }
+        return null;
+    }
+
+    private static SparseArray<List<Tile>> createEmptyTileData() {
+        SparseArray<List<Tile>> newTileData = new SparseArray<>();
+
+        // TODO(dgn): How do we want to handle empty states and sections that have no tiles?
+        // Have an empty list for now that can be rendered as-is without causing issues or too much
+        // state checking. We will have to decide if we want empty lists or no section at all for
+        // the others.
+        newTileData.put(TileSectionType.PERSONALIZED, new ArrayList<>());
+
+        return newTileData;
+    }
+
+    // TODO(dgn): I would like to move that to TileRenderer, but setting the data on the tile,
+    // notifying the observer and updating the tasks make it awkward.
+    private class LargeIconCallbackImpl implements LargeIconBridge.LargeIconCallback {
+        private final SiteSuggestion mSiteData;
+        private final boolean mTrackLoadTask;
+
+        private LargeIconCallbackImpl(SiteSuggestion suggestion, boolean trackLoadTask) {
+            mSiteData = suggestion;
+            mTrackLoadTask = trackLoadTask;
+        }
+
+        @Override
+        public void onLargeIconAvailable(@Nullable Bitmap icon, int fallbackColor,
+                boolean isFallbackColorDefault, @IconType int iconType) {
+            Tile tile = findTile(mSiteData);
+            if (tile != null) { // Do nothing if the tile was removed.
+                tile.setIconType(iconType);
+                if (icon == null) {
+                    mTileRenderer.setTileIconFromColor(tile, fallbackColor, isFallbackColorDefault);
+                } else {
+                    mTileRenderer.setTileIconFromBitmap(tile, icon);
+                }
+
+                mObserver.onTileIconChanged(tile);
+            }
+
+            // This call needs to be made after the tiles are completely initialised, for UMA.
+            if (mTrackLoadTask) removeTask(TileTask.FETCH_ICON);
+        }
+    }
+
+    /**
+     * Implements various listener and delegate interfaces to handle user interactions with tiles.
+     */
+    public class TileInteractionDelegate
+            implements ContextMenuManager.Delegate, OnClickListener, OnCreateContextMenuListener {
+        private final SiteSuggestion mSuggestion;
+        private Runnable mOnClickRunnable;
+
+        public TileInteractionDelegate(SiteSuggestion suggestion) {
+            mSuggestion = suggestion;
+        }
+
+        @Override
+        public void onClick(View view) {
+            Tile tile = findTile(mSuggestion);
+            if (tile == null) return;
+
+            SuggestionsMetrics.recordTileTapped();
+            if (mOnClickRunnable != null) mOnClickRunnable.run();
+            mTileGroupDelegate.openMostVisitedItem(WindowOpenDisposition.CURRENT_TAB, tile);
+        }
+
+        @Override
+        public void openItem(int windowDisposition) {
+            Tile tile = findTile(mSuggestion);
+            if (tile == null) return;
+
+            mTileGroupDelegate.openMostVisitedItem(windowDisposition, tile);
+        }
+
+        @Override
+        public void removeItem() {
+            Tile tile = findTile(mSuggestion);
+            if (tile == null) return;
+
+            // Note: This does not track all the removals, but will track the most recent one. If
+            // that removal is committed, it's good enough for change detection.
+            mPendingRemovalUrl = mSuggestion.url;
+            mTileGroupDelegate.removeMostVisitedItem(tile, url -> mPendingInsertionUrl = url);
+        }
+
+        @Override
+        public String getUrl() {
+            return mSuggestion.url;
+        }
+
+        @Override
+        public String getContextMenuTitle() {
+            return null;
+        }
+
+        @Override
+        public boolean isItemSupported(@ContextMenuItemId int menuItemId) {
+            switch (menuItemId) {
+                // Personalized tiles are the only tiles that can be removed.  Additionally, the
+                // Explore tile counts as a personalized tile but cannot be removed.
+                case ContextMenuItemId.REMOVE:
+                    return mSuggestion.sectionType == TileSectionType.PERSONALIZED
+                            && mSuggestion.source != TileSource.EXPLORE;
+                case ContextMenuItemId.LEARN_MORE:
+                    return SuggestionsConfig.scrollToLoad();
+                case ContextMenuItemId.OPEN_IN_INCOGNITO_TAB:
+                    return mSuggestion.source != TileSource.EXPLORE;
+                default:
+                    return true;
+            }
+        }
+
+        @Override
+        public void onContextMenuCreated() {}
+
+        @Override
+        public void onCreateContextMenu(
+                ContextMenu contextMenu, View view, ContextMenuInfo contextMenuInfo) {
+            mContextMenuManager.createContextMenu(contextMenu, view, this);
+        }
+
+        /**
+         * Set a runnable for click events on the tile. This is primarily used to track interaction
+         * with the tile used by feature engagement purposes.
+         * @param clickRunnable The {@link Runnable} to be executed when tile is clicked.
+         */
+        public void setOnClickRunnable(Runnable clickRunnable) {
+            mOnClickRunnable = clickRunnable;
+        }
+    }
+
+    private class OfflineModelObserver extends SuggestionsOfflineModelObserver<Tile> {
+        public OfflineModelObserver(OfflinePageBridge bridge) {
+            super(bridge);
+        }
+
+        @Override
+        public void onSuggestionOfflineIdChanged(Tile tile, OfflinePageItem item) {
+            boolean oldOfflineAvailable = tile.isOfflineAvailable();
+            tile.setOfflinePageOfflineId(item == null ? null : item.getOfflineId());
+
+            // Only notify to update the view if there will be a visible change.
+            if (oldOfflineAvailable == tile.isOfflineAvailable()) return;
+            mObserver.onTileOfflineBadgeVisibilityChanged(tile);
+        }
+
+        @Override
+        public Iterable<Tile> getOfflinableSuggestions() {
+            List<Tile> tiles = new ArrayList<>();
+            for (int i = 0; i < mTileSections.size(); ++i) tiles.addAll(mTileSections.valueAt(i));
+            return tiles;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
new file mode 100644
index 0000000..b10542c
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
@@ -0,0 +1,137 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.content.Context;
+
+import org.chromium.base.Callback;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ntp.NewTabPageUma;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.snackbar.Snackbar;
+import org.chromium.chrome.browser.snackbar.SnackbarManager;
+import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
+import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory;
+import org.chromium.chrome.browser.suggestions.SuggestionsMetrics;
+import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites;
+import org.chromium.ui.mojom.WindowOpenDisposition;
+
+import java.util.List;
+
+/**
+ * Reusable implementation of {@link TileGroup.Delegate}. Performs work in parts of the system that
+ * the {@link TileGroup} should not know about.
+ */
+public class TileGroupDelegateImpl implements TileGroup.Delegate {
+    private final Context mContext;
+    private final SnackbarManager mSnackbarManager;
+    private final SuggestionsNavigationDelegate mNavigationDelegate;
+    private final MostVisitedSites mMostVisitedSites;
+
+    private boolean mIsDestroyed;
+    private SnackbarController mTileRemovedSnackbarController;
+
+    public TileGroupDelegateImpl(ChromeActivity activity, Profile profile,
+            SuggestionsNavigationDelegate navigationDelegate, SnackbarManager snackbarManager) {
+        mContext = activity;
+        mSnackbarManager = snackbarManager;
+        mNavigationDelegate = navigationDelegate;
+        mMostVisitedSites =
+                SuggestionsDependencyFactory.getInstance().createMostVisitedSites(profile);
+    }
+
+    @Override
+    public void removeMostVisitedItem(Tile item, Callback<String> removalUndoneCallback) {
+        assert !mIsDestroyed;
+
+        mMostVisitedSites.addBlacklistedUrl(item.getUrl());
+        showTileRemovedSnackbar(item.getUrl(), removalUndoneCallback);
+    }
+
+    @Override
+    public void openMostVisitedItem(int windowDisposition, Tile item) {
+        assert !mIsDestroyed;
+
+        String url = item.getUrl();
+
+        // TODO(treib): Should we call recordOpenedMostVisitedItem here?
+        if (windowDisposition != WindowOpenDisposition.NEW_WINDOW) {
+            recordOpenedTile(item);
+        }
+
+        mNavigationDelegate.navigateToSuggestionUrl(windowDisposition, url);
+    }
+
+    @Override
+    public void setMostVisitedSitesObserver(MostVisitedSites.Observer observer, int maxResults) {
+        assert !mIsDestroyed;
+
+        mMostVisitedSites.setObserver(observer, maxResults);
+    }
+
+    @Override
+    public void onLoadingComplete(List<Tile> tiles) {
+        // This method is called after network calls complete. It could happen after the suggestions
+        // surface is destroyed.
+        if (mIsDestroyed) return;
+
+        for (Tile tile : tiles) {
+            mMostVisitedSites.recordTileImpression(tile);
+        }
+
+        mMostVisitedSites.recordPageImpression(tiles.size());
+
+        for (Tile tile : tiles) {
+            if (tile.isOfflineAvailable()) {
+                SuggestionsMetrics.recordTileOfflineAvailability(tile.getIndex());
+            }
+        }
+    }
+
+    @Override
+    public void destroy() {
+        assert !mIsDestroyed;
+        mIsDestroyed = true;
+
+        if (mTileRemovedSnackbarController != null) {
+            mSnackbarManager.dismissSnackbars(mTileRemovedSnackbarController);
+        }
+        mMostVisitedSites.destroy();
+    }
+
+    private void showTileRemovedSnackbar(String url, final Callback<String> removalUndoneCallback) {
+        if (mTileRemovedSnackbarController == null) {
+            mTileRemovedSnackbarController = new SnackbarController() {
+                @Override
+                public void onDismissNoAction(Object actionData) {}
+
+                /** Undoes the tile removal. */
+                @Override
+                public void onAction(Object actionData) {
+                    if (mIsDestroyed) return;
+                    String url = (String) actionData;
+                    removalUndoneCallback.onResult(url);
+                    mMostVisitedSites.removeBlacklistedUrl(url);
+                }
+            };
+        }
+        Snackbar snackbar = Snackbar.make(mContext.getString(R.string.most_visited_item_removed),
+                                            mTileRemovedSnackbarController, Snackbar.TYPE_ACTION,
+                                            Snackbar.UMA_NTP_MOST_VISITED_DELETE_UNDO)
+                                    .setAction(mContext.getString(R.string.undo), url);
+        mSnackbarManager.showSnackbar(snackbar);
+    }
+
+    private void recordOpenedTile(Tile tile) {
+        NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_MOST_VISITED_TILE);
+        RecordUserAction.record("MobileNTPMostVisited");
+        NewTabPageUma.recordExplicitUserNavigation(
+                tile.getUrl(), NewTabPageUma.RAPPOR_ACTION_VISITED_SUGGESTED_TILE);
+        mMostVisitedSites.recordOpenedMostVisitedItem(tile);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
new file mode 100644
index 0000000..ef0b0b90
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
@@ -0,0 +1,284 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.support.annotation.LayoutRes;
+import android.support.graphics.drawable.VectorDrawableCompat;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Log;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.task.AsyncTask;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
+import org.chromium.chrome.browser.explore_sites.ExploreSitesIPH;
+import org.chromium.chrome.browser.explore_sites.MostLikelyVariation;
+import org.chromium.chrome.browser.favicon.IconType;
+import org.chromium.chrome.browser.favicon.LargeIconBridge;
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.suggestions.ImageFetcher;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle;
+import org.chromium.chrome.browser.util.ViewUtils;
+import org.chromium.chrome.browser.widget.RoundedIconGenerator;
+import org.chromium.components.feature_engagement.EventConstants;
+import org.chromium.components.feature_engagement.Tracker;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class that renders {@link Tile}s into a provided {@link ViewGroup}, creating and
+ * manipulating the views as needed.
+ */
+public class TileRenderer {
+    private static final String TAG = "TileRenderer";
+
+    private final Resources mResources;
+    private final ImageFetcher mImageFetcher;
+    private final RoundedIconGenerator mIconGenerator;
+    private final Resources.Theme mTheme;
+
+    @TileStyle
+    private final int mStyle;
+    private final int mTitleLinesCount;
+    private final int mDesiredIconSize;
+    private final int mMinIconSize;
+    private final float mIconCornerRadius;
+
+    @LayoutRes
+    private final int mLayout;
+
+    @LayoutRes
+    private final int mTopSitesLayout;
+
+    public TileRenderer(
+            Context context, @TileStyle int style, int titleLines, ImageFetcher imageFetcher) {
+        mImageFetcher = imageFetcher;
+        mStyle = style;
+        mTitleLinesCount = titleLines;
+
+        mResources = context.getResources();
+        mTheme = context.getTheme();
+        mDesiredIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_size);
+        mIconCornerRadius = mResources.getDimension(R.dimen.tile_view_icon_corner_radius);
+        int minIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_min_size);
+
+        // On ldpi devices, mDesiredIconSize could be even smaller than the global limit.
+        mMinIconSize = Math.min(mDesiredIconSize, minIconSize);
+
+        mLayout = getLayout();
+        mTopSitesLayout = getTopSitesLayout();
+
+        int iconColor = ApiCompatibilityUtils.getColor(
+                mResources, R.color.default_favicon_background_color);
+        int iconTextSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_text_size);
+        mIconGenerator = new RoundedIconGenerator(
+                mDesiredIconSize, mDesiredIconSize, mDesiredIconSize / 2, iconColor, iconTextSize);
+    }
+
+    /**
+     * Renders tile views in the given {@link ViewGroup}, reusing existing tile views where
+     * possible because view inflation and icon loading are slow.
+     * @param parent The layout to render the tile views into.
+     * @param sectionTiles Tiles to render.
+     * @param setupDelegate Delegate used to setup callbacks and listeners for the new views.
+     */
+    public void renderTileSection(
+            List<Tile> sectionTiles, ViewGroup parent, TileGroup.TileSetupDelegate setupDelegate) {
+        // Map the old tile views by url so they can be reused later.
+        Map<SiteSuggestion, SuggestionsTileView> oldTileViews = new HashMap<>();
+        int childCount = parent.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            SuggestionsTileView tileView = (SuggestionsTileView) parent.getChildAt(i);
+            oldTileViews.put(tileView.getData(), tileView);
+        }
+
+        // Remove all views from the layout because even if they are reused later they'll have to be
+        // added back in the correct order.
+        parent.removeAllViews();
+
+        for (Tile tile : sectionTiles) {
+            SuggestionsTileView tileView = oldTileViews.get(tile.getData());
+            if (tileView == null) {
+                tileView = buildTileView(tile, parent, setupDelegate);
+            }
+
+            parent.addView(tileView);
+        }
+    }
+
+    /**
+     * Record that a tile was clicked for IPH reasons.
+     */
+    private void recordTileClickedForIPH(String eventName) {
+        Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
+        tracker.notifyEvent(eventName);
+    }
+
+    /**
+     * Inflates a new tile view, initializes it, and loads an icon for it.
+     * @param tile The tile that holds the data to populate the new tile view.
+     * @param parentView The parent of the new tile view.
+     * @param setupDelegate The delegate used to setup callbacks and listeners for the new view.
+     * @return The new tile view.
+     */
+    @VisibleForTesting
+    SuggestionsTileView buildTileView(
+            Tile tile, ViewGroup parentView, TileGroup.TileSetupDelegate setupDelegate) {
+        SuggestionsTileView tileView;
+
+        if (tile.getSource() == TileSource.EXPLORE) {
+            tileView = (TopSitesTileView) LayoutInflater.from(parentView.getContext())
+                               .inflate(mTopSitesLayout, parentView, false);
+
+            int iconVariation = ExploreSitesBridge.getIconVariation();
+            if (iconVariation == MostLikelyVariation.ICON_ARROW) {
+                tile.setIcon(VectorDrawableCompat.create(
+                        mResources, R.drawable.ic_arrow_forward_blue_24dp, mTheme));
+                tile.setType(TileVisualType.ICON_REAL);
+            } else if (iconVariation == MostLikelyVariation.ICON_DOTS) {
+                tile.setIcon(VectorDrawableCompat.create(
+                        mResources, R.drawable.ic_apps_blue_24dp, mTheme));
+                tile.setType(TileVisualType.ICON_REAL);
+            } else if (iconVariation == MostLikelyVariation.ICON_GROUPED) {
+                tile.setIcon(VectorDrawableCompat.create(
+                        mResources, R.drawable.ic_apps_blue_24dp, mTheme));
+                tile.setType(TileVisualType.ICON_DEFAULT);
+
+                // One task to load actual icon.
+                LargeIconBridge.LargeIconCallback bridgeCallback =
+                        setupDelegate.createIconLoadCallback(tile);
+                ExploreSitesBridge.getSummaryImage(Profile.getLastUsedProfile(), mDesiredIconSize,
+                        (Bitmap img)
+                                -> bridgeCallback.onLargeIconAvailable(
+                                        img, Color.BLACK, false, IconType.FAVICON));
+            }
+        } else {
+            tileView = (SuggestionsTileView) LayoutInflater.from(parentView.getContext())
+                               .inflate(mLayout, parentView, false);
+        }
+
+        tileView.initialize(tile, mTitleLinesCount);
+
+        // Note: It is important that the callbacks below don't keep a reference to the tile or
+        // modify them as there is no guarantee that the same tile would be used to update the view.
+        if (tile.getSource() != TileSource.EXPLORE) {
+            fetchIcon(tile.getData(), setupDelegate.createIconLoadCallback(tile));
+        }
+
+        TileGroup.TileInteractionDelegate delegate = setupDelegate.createInteractionDelegate(tile);
+        if (tile.getSource() == TileSource.HOMEPAGE) {
+            delegate.setOnClickRunnable(
+                    () -> recordTileClickedForIPH(EventConstants.HOMEPAGE_TILE_CLICKED));
+        } else if (tile.getSource() == TileSource.EXPLORE) {
+            delegate.setOnClickRunnable(
+                    () -> recordTileClickedForIPH(EventConstants.EXPLORE_SITES_TILE_TAPPED));
+        }
+
+        tileView.setOnClickListener(delegate);
+        tileView.setOnCreateContextMenuListener(delegate);
+
+        if (tile.getSource() == TileSource.EXPLORE) {
+            ExploreSitesIPH.configureIPH(tileView, Profile.getLastUsedProfile());
+        }
+
+        return tileView;
+    }
+
+    private void fetchIcon(
+            final SiteSuggestion siteData, final LargeIconBridge.LargeIconCallback iconCallback) {
+        if (siteData.whitelistIconPath.isEmpty()) {
+            mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback);
+            return;
+        }
+
+        AsyncTask<Bitmap> task = new AsyncTask<Bitmap>() {
+            @Override
+            protected Bitmap doInBackground() {
+                Bitmap bitmap = BitmapFactory.decodeFile(siteData.whitelistIconPath);
+                if (bitmap == null) {
+                    Log.d(TAG, "Image decoding failed: %s", siteData.whitelistIconPath);
+                }
+                return bitmap;
+            }
+
+            @Override
+            protected void onPostExecute(Bitmap icon) {
+                if (icon == null) {
+                    mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback);
+                } else {
+                    iconCallback.onLargeIconAvailable(icon, Color.BLACK, false, IconType.INVALID);
+                }
+            }
+        };
+        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
+
+    public void updateIcon(
+            SiteSuggestion siteData, LargeIconBridge.LargeIconCallback iconCallback) {
+        mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback);
+    }
+
+    public void setTileIconFromBitmap(Tile tile, Bitmap icon) {
+        int radius = Math.round(mIconCornerRadius * icon.getWidth() / mDesiredIconSize);
+        if (tile.getSource() == TileSource.EXPLORE) {
+            radius = mDesiredIconSize / 2;
+        }
+        RoundedBitmapDrawable roundedIcon = ViewUtils.createRoundedBitmapDrawable(icon, radius);
+        roundedIcon.setAntiAlias(true);
+        roundedIcon.setFilterBitmap(true);
+
+        tile.setIcon(roundedIcon);
+        tile.setType(TileVisualType.ICON_REAL);
+    }
+
+    public void setTileIconFromColor(Tile tile, int fallbackColor, boolean isFallbackColorDefault) {
+        // Explore should not have generated icons.
+        if (tile.getSource() == TileSource.EXPLORE) {
+            return;
+        }
+        mIconGenerator.setBackgroundColor(fallbackColor);
+        Bitmap icon = mIconGenerator.generateIconForUrl(tile.getUrl());
+        tile.setIcon(new BitmapDrawable(mResources, icon));
+        tile.setType(
+                isFallbackColorDefault ? TileVisualType.ICON_DEFAULT : TileVisualType.ICON_COLOR);
+    }
+
+    @LayoutRes
+    private int getLayout() {
+        switch (mStyle) {
+            case TileStyle.MODERN:
+                return R.layout.suggestions_tile_view;
+            case TileStyle.MODERN_CONDENSED:
+                return R.layout.suggestions_tile_view_condensed;
+        }
+        assert false;
+        return 0;
+    }
+
+    @LayoutRes
+    private int getTopSitesLayout() {
+        switch (mStyle) {
+            case TileStyle.MODERN:
+                return R.layout.top_sites_tile_view;
+            case TileStyle.MODERN_CONDENSED:
+                return R.layout.top_sites_tile_view_condensed;
+        }
+        assert false;
+        return 0;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java
index 31b6c51e..423058c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java
@@ -10,9 +10,6 @@
 
 import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
 import org.chromium.chrome.browser.explore_sites.MostLikelyVariation;
-import org.chromium.chrome.browser.suggestions.SuggestionsTileView;
-import org.chromium.chrome.browser.suggestions.Tile;
-import org.chromium.chrome.browser.suggestions.TileVisualType;
 
 /**
  * The view for a top sites tile. Displays the title of the site beneath a large icon.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 1abdcd5..dd5d06c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -57,8 +57,6 @@
 import org.chromium.chrome.browser.device.DeviceClassManager;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.ntp.NewTabPage;
-import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
-import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper.MenuButtonState;
 import org.chromium.chrome.browser.omnibox.LocationBar;
 import org.chromium.chrome.browser.omnibox.LocationBarPhone;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
@@ -176,8 +174,6 @@
     @ViewDebug.ExportedProperty(category = "chrome")
     private Rect mClipRect;
 
-    private OnClickListener mNewTabListener;
-
     @ViewDebug.ExportedProperty(category = "chrome")
     protected boolean mUrlFocusChangeInProgress;
 
@@ -2684,16 +2680,6 @@
         mExperimentalButton.setTranslationX(0);
     }
 
-    @VisibleForTesting
-    public View getExperimentalButtonForTesting() {
-        return mExperimentalButton;
-    }
-
-    @VisibleForTesting
-    public void endExperimentalButtonAnimationForTesting() {
-        if (mExperimentalButtonAnimator != null) mExperimentalButtonAnimator.end();
-    }
-
     private void setTabSwitcherAnimationMenuDrawable() {
         mTabSwitcherAnimationMenuDrawable =
                 ApiCompatibilityUtils
@@ -2702,24 +2688,6 @@
         ((BitmapDrawable) mTabSwitcherAnimationMenuDrawable).setGravity(Gravity.CENTER);
     }
 
-    private void setTabSwitcherAnimationMenuBadgeDrawable() {
-        MenuButtonState buttonState = UpdateMenuItemHelper.getInstance().getUiState().buttonState;
-        if (buttonState == null) return;
-
-        Drawable darkDrawable =
-                ApiCompatibilityUtils.getDrawable(getResources(), buttonState.darkBadgeIcon);
-        Drawable lightDrawable =
-                ApiCompatibilityUtils.getDrawable(getResources(), buttonState.lightBadgeIcon);
-
-        mTabSwitcherAnimationMenuBadgeDarkDrawable = darkDrawable;
-        mTabSwitcherAnimationMenuBadgeDarkDrawable.mutate();
-        ((BitmapDrawable) mTabSwitcherAnimationMenuBadgeDarkDrawable).setGravity(Gravity.CENTER);
-
-        mTabSwitcherAnimationMenuBadgeLightDrawable = lightDrawable;
-        mTabSwitcherAnimationMenuBadgeLightDrawable.mutate();
-        ((BitmapDrawable) mTabSwitcherAnimationMenuBadgeLightDrawable).setGravity(Gravity.CENTER);
-    }
-
     /**
      * Custom drawable that allows sharing the NTP search box drawable between the toolbar and the
      * NTP.  This allows animations to continue as the drawable is switched between the two owning
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 4c679bd..24e57db 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2294,6 +2294,9 @@
       <message name="IDS_MENU_ADD_TO_APPS" desc="Text to accompany icon that will navigate to a page showing a categorized view of different applications or sites">
         Add to my apps
       </message>
+      <message name="IDS_ADDED_TO_APPS" desc="Text that confirms a site or app has been added to the apps screen.">
+        <ph name="APP_NAME">%1$s<ex>Zomato</ex></ph> added to my apps
+      </message>
 
       <!-- Page info popup -->
       <message name="IDS_PAGE_INFO_SITE_SETTINGS_BUTTON" desc="Text in the button that opens a website's Site Settings from the Page Info dialog.">
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
index 1fbd37e..0526e6bd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
@@ -24,6 +24,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -37,6 +38,7 @@
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.concurrent.TimeoutException;
 
@@ -232,7 +234,7 @@
     @Test
     @SmallTest
     @Feature({"Browser", "Main"})
-    @DisabledTest(message="https://crbug.com/970184")
+    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
     public void testHideMenuOnToggleOverview() throws TimeoutException, InterruptedException {
         CallbackHelper overviewModeFinishedShowingCallback = new CallbackHelper();
         OverviewModeBehavior.OverviewModeObserver overviewModeObserver =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
index 9fc32fe..30d29ed 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
@@ -53,8 +53,8 @@
 import org.chromium.chrome.test.util.ViewUtils;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.EmbeddedTestServer;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageLoadTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageLoadTest.java
index 099daa9..ae514f1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageLoadTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageLoadTest.java
@@ -24,14 +24,14 @@
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.favicon.IconType;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
-import org.chromium.chrome.browser.suggestions.Tile;
-import org.chromium.chrome.browser.suggestions.TileVisualType;
+import org.chromium.chrome.browser.suggestions.tile.Tile;
+import org.chromium.chrome.browser.suggestions.tile.TileVisualType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.NewTabPageTestUtils;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
 import org.chromium.net.test.EmbeddedTestServer;
 
 import java.io.IOException;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index a5ccc39f..345118c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -76,8 +76,8 @@
 import org.chromium.chrome.test.util.RenderTestRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java
index b6edce5..b5d78a2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java
@@ -8,7 +8,7 @@
 
 import static org.chromium.base.test.util.UrlUtils.getTestFilePath;
 import static org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge.createOfflinePageItem;
-import static org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites.createSiteSuggestion;
+import static org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites.createSiteSuggestion;
 
 import android.graphics.Bitmap;
 
@@ -23,9 +23,9 @@
 import org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge;
 import org.chromium.chrome.test.util.browser.suggestions.ContentSuggestionsTestUtils;
 import org.chromium.chrome.test.util.browser.suggestions.DummySuggestionsEventReporter;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
 import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.util.Arrays;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
index ad112baf..086ce3f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
@@ -45,9 +45,9 @@
 import org.chromium.chrome.test.util.NewTabPageTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
 import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.net.test.EmbeddedTestServer;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetNoTilesUiCaptureTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetNoTilesUiCaptureTest.java
index 48fc3d8..47fefa6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetNoTilesUiCaptureTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetNoTilesUiCaptureTest.java
@@ -21,8 +21,8 @@
 import org.chromium.chrome.browser.test.ScreenShooter;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
 import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
 import org.chromium.ui.test.util.UiRestriction;
 
 /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java
deleted file mode 100644
index e520cbb8..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java
+++ /dev/null
@@ -1,332 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.greaterThan;
-
-import static org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites.createSiteSuggestion;
-
-import android.app.Activity;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.support.annotation.Nullable;
-import android.support.test.espresso.matcher.ViewMatchers;
-import android.support.test.filters.MediumTest;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.RetryOnFailure;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeApplication;
-import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.ntp.NewTabPage;
-import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
-import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.util.ViewUtils;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.NewTabPageTestUtils;
-import org.chromium.chrome.test.util.RenderTestRule;
-import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
-import org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
-import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
-import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
-import org.chromium.content_public.browser.test.util.Criteria;
-import org.chromium.content_public.browser.test.util.CriteriaHelper;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.net.test.EmbeddedTestServerRule;
-import org.chromium.ui.modelutil.ListObservable;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Instrumentation tests for the {@link TileGridLayout} on the New Tab Page.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
-public class TileGridLayoutTest {
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
-
-    @Rule
-    public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule();
-
-    @Rule
-    public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule();
-
-    @Rule
-    public RenderTestRule mRenderTestRule = new RenderTestRule();
-
-    private static final String[] FAKE_MOST_VISITED_URLS = new String[] {
-            "/chrome/test/data/android/navigate/one.html",
-            "/chrome/test/data/android/navigate/two.html",
-            "/chrome/test/data/android/navigate/three.html",
-            "/chrome/test/data/android/navigate/four.html",
-            "/chrome/test/data/android/navigate/five.html",
-            "/chrome/test/data/android/navigate/six.html",
-            "/chrome/test/data/android/navigate/seven.html",
-            "/chrome/test/data/android/navigate/eight.html",
-            "/chrome/test/data/android/navigate/nine.html",
-    };
-
-    private static final String[] FAKE_MOST_VISITED_TITLES =
-            new String[] {"ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"};
-
-    private final CallbackHelper mLoadCompleteHelper = new CallbackHelper();
-
-    @Test
-    @MediumTest
-    @Feature({"NewTabPage", "RenderTest"})
-    // TODO(https://crbug.com/906151): Add new goldens and enable ExploreSites.
-    @DisableFeatures(ChromeFeatureList.EXPLORE_SITES)
-    public void testTileGridAppearance() throws Exception {
-        NewTabPage ntp = setUpFakeDataToShowOnNtp(FAKE_MOST_VISITED_URLS.length);
-        mRenderTestRule.render(getTileGridLayout(ntp), "ntp_tile_grid_layout");
-    }
-
-    @Test
-    //@MediumTest
-    @DisabledTest(message = "crbug.com/771648")
-    @Feature({"NewTabPage", "RenderTest"})
-    public void testModernTileGridAppearance_Full() throws IOException, InterruptedException {
-        View tileGridLayout = renderTiles(makeSuggestions(FAKE_MOST_VISITED_URLS.length));
-
-        setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, mActivityTestRule.getActivity());
-        mRenderTestRule.render(tileGridLayout, "modern_full_grid_portrait");
-
-        setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, mActivityTestRule.getActivity());
-        mRenderTestRule.render(tileGridLayout, "modern_full_grid_landscape");
-
-        // In landscape, modern tiles should use all available space.
-        int tileGridMaxWidthPx = tileGridLayout.getResources().getDimensionPixelSize(
-                R.dimen.tile_grid_layout_max_width);
-        if (((FrameLayout) tileGridLayout.getParent()).getMeasuredWidth() > tileGridMaxWidthPx) {
-            assertThat(tileGridLayout.getMeasuredWidth(), greaterThan(tileGridMaxWidthPx));
-        }
-    }
-
-    @Test
-    //@MediumTest
-    @DisabledTest(message = "crbug.com/771648")
-    @RetryOnFailure
-    @Feature({"NewTabPage", "RenderTest"})
-    public void testModernTileGridAppearance_Two() throws IOException, InterruptedException {
-        View tileGridLayout = renderTiles(makeSuggestions(2));
-
-        setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, mActivityTestRule.getActivity());
-        mRenderTestRule.render(tileGridLayout, "modern_two_tiles_grid_portrait");
-
-        setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, mActivityTestRule.getActivity());
-        mRenderTestRule.render(tileGridLayout, "modern_two_tiles_grid_landscape");
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"NewTabPage", "RenderTest"})
-    // TODO(https://crbug.com/906151): Add new goldens and enable ExploreSites.
-    @DisableFeatures(ChromeFeatureList.EXPLORE_SITES)
-    public void testTileAppearanceModern()
-            throws IOException, InterruptedException, TimeoutException {
-        List<SiteSuggestion> suggestions = makeSuggestions(2);
-        List<String> offlineAvailableUrls = Collections.singletonList(suggestions.get(0).url);
-        ViewGroup tiles = renderTiles(suggestions, offlineAvailableUrls);
-
-        mLoadCompleteHelper.waitForCallback(0);
-
-        mRenderTestRule.render(tiles.getChildAt(0), "tile_modern_offline");
-        mRenderTestRule.render(tiles.getChildAt(1), "tile_modern");
-    }
-
-    private List<SiteSuggestion> makeSuggestions(int count) {
-        List<SiteSuggestion> siteSuggestions = new ArrayList<>(count);
-
-        assertEquals(FAKE_MOST_VISITED_URLS.length, FAKE_MOST_VISITED_TITLES.length);
-        assertTrue(count <= FAKE_MOST_VISITED_URLS.length);
-
-        for (int i = 0; i < count; i++) {
-            String url = mTestServerRule.getServer().getURL(FAKE_MOST_VISITED_URLS[i]);
-            siteSuggestions.add(createSiteSuggestion(FAKE_MOST_VISITED_TITLES[i], url));
-        }
-
-        return siteSuggestions;
-    }
-
-    private NewTabPage setUpFakeDataToShowOnNtp(int suggestionCount) throws InterruptedException {
-        List<SiteSuggestion> siteSuggestions = makeSuggestions(suggestionCount);
-
-        FakeMostVisitedSites mMostVisitedSites = new FakeMostVisitedSites();
-        mMostVisitedSites.setTileSuggestions(siteSuggestions);
-        mSuggestionsDeps.getFactory().mostVisitedSites = mMostVisitedSites;
-
-        mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource();
-
-        mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
-
-        Tab mTab = mActivityTestRule.getActivity().getActivityTab();
-        NewTabPageTestUtils.waitForNtpLoaded(mTab);
-
-        assertTrue(mTab.getNativePage() instanceof NewTabPage);
-        NewTabPage ntp = (NewTabPage) mTab.getNativePage();
-
-        org.chromium.chrome.test.util.ViewUtils.waitForView(
-                (ViewGroup) ntp.getView(), ViewMatchers.withId(R.id.tile_grid_layout));
-
-        return ntp;
-    }
-
-    private void setOrientation(final int requestedOrientation, final Activity activity) {
-        if (orientationMatchesRequest(activity, requestedOrientation)) return;
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> activity.setRequestedOrientation(requestedOrientation));
-
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                return orientationMatchesRequest(activity, requestedOrientation);
-            }
-        });
-    }
-
-    /**
-     * Checks whether the requested orientation matches the current one.
-     * @param activity Activity to check the orientation from. We pull its {@link Configuration} and
-     *         content {@link View}.
-     * @param requestedOrientation The requested orientation, as used in
-     *         {@link ActivityInfo#screenOrientation}.
-     */
-    private boolean orientationMatchesRequest(Activity activity, int requestedOrientation) {
-        // Note: Requests use a constant from ActivityInfo, not Configuration.ORIENTATION_*!
-        boolean expectLandscape = requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-
-        // We check the orientation by looking at the dimensions of the content view. Looking at
-        // orientation from the configuration is not reliable as sometimes the activity gets the
-        // event that its configuration changed, but has not updated its layout yet.
-        Configuration configuration = activity.getResources().getConfiguration();
-        View contentView = activity.findViewById(android.R.id.content);
-        int smallestWidthPx = ViewUtils.dpToPx(activity, configuration.smallestScreenWidthDp);
-        boolean viewIsLandscape = contentView.getMeasuredWidth() > smallestWidthPx;
-
-        return expectLandscape == viewIsLandscape;
-    }
-
-    private TileGridLayout getTileGridLayout(NewTabPage ntp) {
-        TileGridLayout tileGridLayout = ntp.getView().findViewById(R.id.tile_grid_layout);
-        assertNotNull("Unable to retrieve the TileGridLayout.", tileGridLayout);
-        return tileGridLayout;
-    }
-
-    /**
-     * Starts and sets up an activity to render the provided site suggestions in the activity.
-     * @return the layout in which the suggestions are rendered.
-     */
-    private TileGridLayout renderTiles(List<SiteSuggestion> siteSuggestions,
-            List<String> offlineUrls) throws IOException, InterruptedException {
-        // Launching the activity, that should now use the right UI.
-        mActivityTestRule.startMainActivityOnBlankPage();
-        ChromeActivity activity = mActivityTestRule.getActivity();
-
-        // Setting up the dummy data.
-        FakeMostVisitedSites mostVisitedSites = new FakeMostVisitedSites();
-        mostVisitedSites.setTileSuggestions(siteSuggestions);
-        mSuggestionsDeps.getFactory().mostVisitedSites = mostVisitedSites;
-        mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource();
-
-        FrameLayout contentView = new FrameLayout(activity);
-        UiConfig uiConfig = new UiConfig(contentView);
-
-        return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
-            activity.setContentView(contentView);
-
-            SiteSectionViewHolder viewHolder = SiteSection.createViewHolder(
-                    SiteSection.inflateSiteSection(contentView), uiConfig);
-
-            uiConfig.updateDisplayStyle();
-
-            SiteSection siteSection = createSiteSection(viewHolder, uiConfig, offlineUrls);
-            siteSection.getTileGroupForTesting().onSwitchToForeground(false);
-            assertTrue("Tile Data should be visible.", siteSection.isVisible());
-
-            siteSection.onBindViewHolder(viewHolder, 0);
-            contentView.addView(viewHolder.itemView);
-
-            return (TileGridLayout) viewHolder.itemView;
-        });
-    }
-
-    private TileGridLayout renderTiles(List<SiteSuggestion> siteSuggestions)
-            throws IOException, InterruptedException {
-        return renderTiles(siteSuggestions, Collections.emptyList());
-    }
-
-    private SiteSection createSiteSection(
-            final SiteSectionViewHolder viewHolder, UiConfig uiConfig, List<String> offlineUrls) {
-        ThreadUtils.assertOnUiThread();
-
-        ChromeActivity activity = mActivityTestRule.getActivity();
-
-        Profile profile = Profile.getLastUsedProfile();
-        SuggestionsUiDelegate uiDelegate = new SuggestionsUiDelegateImpl(
-                mSuggestionsDeps.getFactory().createSuggestionSource(null),
-                mSuggestionsDeps.getFactory().createEventReporter(), null, profile, null,
-                ChromeApplication.getReferencePool(), activity.getSnackbarManager());
-
-        FakeOfflinePageBridge offlinePageBridge = new FakeOfflinePageBridge();
-        List<OfflinePageItem> offlinePageItems = new ArrayList<>();
-        for (int i = 0; i < offlineUrls.size(); i++) {
-            offlinePageItems.add(
-                    FakeOfflinePageBridge.createOfflinePageItem(offlineUrls.get(i), i + 1L));
-        }
-        offlinePageBridge.setItems(offlinePageItems);
-        offlinePageBridge.setIsOfflinePageModelLoaded(true);
-
-        TileGroup.Delegate delegate = new TileGroupDelegateImpl(activity, profile, null, null) {
-            @Override
-            public void onLoadingComplete(List<Tile> tiles) {
-                super.onLoadingComplete(tiles);
-                mLoadCompleteHelper.notifyCalled();
-            }
-        };
-
-        SiteSection siteSection =
-                new SiteSection(uiDelegate, null, delegate, offlinePageBridge, uiConfig);
-
-        siteSection.addObserver(new ListObservable.ListObserver<PartialBindCallback>() {
-            @Override
-            public void onItemRangeChanged(ListObservable child, int index, int count,
-                    @Nullable PartialBindCallback payload) {
-                if (payload != null) payload.onResult(viewHolder);
-            }
-        });
-
-        return siteSection;
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java
deleted file mode 100644
index 634ce4a..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.espresso.matcher.ViewMatchers;
-import android.support.test.filters.MediumTest;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.RetryOnFailure;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.native_page.ContextMenuManager;
-import org.chromium.chrome.browser.ntp.NewTabPage;
-import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView;
-import org.chromium.chrome.browser.snackbar.SnackbarManager;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.NewTabPageTestUtils;
-import org.chromium.chrome.test.util.ViewUtils;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
-import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
-import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
-import org.chromium.content_public.browser.test.util.Criteria;
-import org.chromium.content_public.browser.test.util.CriteriaHelper;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.content_public.browser.test.util.TestTouchUtils;
-import org.chromium.net.test.EmbeddedTestServer;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Instrumentation tests for {@link TileGroup} on the New Tab Page.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@RetryOnFailure
-public class TileGroupTest {
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
-
-    @Rule
-    public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule();
-
-    private static final String[] FAKE_MOST_VISITED_URLS =
-            new String[] {"/chrome/test/data/android/navigate/one.html",
-                    "/chrome/test/data/android/navigate/two.html",
-                    "/chrome/test/data/android/navigate/three.html"};
-
-    private NewTabPage mNtp;
-    private String[] mSiteSuggestionUrls;
-    private FakeMostVisitedSites mMostVisitedSites;
-    private EmbeddedTestServer mTestServer;
-
-    @Before
-    public void setUp() throws Exception {
-        mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
-
-        mSiteSuggestionUrls = mTestServer.getURLs(FAKE_MOST_VISITED_URLS);
-
-        mMostVisitedSites = new FakeMostVisitedSites();
-        mSuggestionsDeps.getFactory().mostVisitedSites = mMostVisitedSites;
-        mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls);
-
-        mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource();
-        initializeTab();
-    }
-
-    public void initializeTab() throws Exception {
-        mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
-        Tab mTab = mActivityTestRule.getActivity().getActivityTab();
-        NewTabPageTestUtils.waitForNtpLoaded(mTab);
-
-        Assert.assertTrue(mTab.getNativePage() instanceof NewTabPage);
-        mNtp = (NewTabPage) mTab.getNativePage();
-
-        ViewUtils.waitForView(
-                (ViewGroup) mNtp.getView(), ViewMatchers.withId(R.id.tile_grid_layout));
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mTestServer.stopAndDestroyServer();
-
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"NewTabPage"})
-    public void testDismissTileWithContextMenu() throws Exception {
-        SiteSuggestion siteToDismiss = mMostVisitedSites.getCurrentSites().get(0);
-        final View tileView = getTileViewFor(siteToDismiss);
-
-        // Dismiss the tile using the context menu.
-        invokeContextMenu(tileView, ContextMenuManager.ContextMenuItemId.REMOVE);
-        Assert.assertTrue(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0]));
-
-        // Ensure that the removal is reflected in the ui.
-        Assert.assertEquals(3, getTileGridLayout().getChildCount());
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls[1], mSiteSuggestionUrls[2]);
-        });
-        waitForTileRemoved(siteToDismiss);
-        Assert.assertEquals(2, getTileGridLayout().getChildCount());
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"NewTabPage"})
-    public void testDismissExploreTileWithContextMenuFails() throws Exception {
-        SiteSuggestion exploreTile = recreateSuggestionsWithExploreTile();
-        initializeTab();
-
-        Assert.assertEquals(4, getTileGridLayout().getChildCount());
-
-        final View tileView = getTileViewFor(exploreTile);
-        TestTouchUtils.performLongClickOnMainSync(
-                InstrumentationRegistry.getInstrumentation(), tileView);
-        Assert.assertFalse(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction(
-                mActivityTestRule.getActivity(), ContextMenuManager.ContextMenuItemId.REMOVE, 0));
-        Assert.assertEquals(4, getTileGridLayout().getChildCount());
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"NewTabPage"})
-    public void testDismissTileUndo() throws Exception {
-        SiteSuggestion siteToDismiss = mMostVisitedSites.getCurrentSites().get(0);
-        final ViewGroup tileContainer = getTileGridLayout();
-        final View tileView = getTileViewFor(siteToDismiss);
-        Assert.assertEquals(3, tileContainer.getChildCount());
-
-        // Dismiss the tile using the context menu.
-        invokeContextMenu(tileView, ContextMenuManager.ContextMenuItemId.REMOVE);
-
-        // Ensure that the removal update goes through.
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls[1], mSiteSuggestionUrls[2]);
-        });
-        waitForTileRemoved(siteToDismiss);
-        Assert.assertEquals(2, tileContainer.getChildCount());
-        final View snackbarButton = waitForSnackbar(mActivityTestRule.getActivity());
-
-        Assert.assertTrue(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0]));
-        TestThreadUtils.runOnUiThreadBlocking(() -> { snackbarButton.callOnClick(); });
-
-        Assert.assertFalse(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0]));
-
-        // Ensure that the removal of the update goes through.
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> { mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls); });
-        waitForTileAdded(siteToDismiss);
-        Assert.assertEquals(3, tileContainer.getChildCount());
-    }
-
-    private NewTabPageRecyclerView getRecyclerView() {
-        return mNtp.getNewTabPageView().getRecyclerView();
-    }
-
-    private TileGridLayout getTileGridLayout() {
-        ViewGroup newTabPageLayout = mNtp.getNewTabPageLayout();
-        Assert.assertNotNull("Unable to retrieve the NewTabPageLayout.", newTabPageLayout);
-
-        TileGridLayout tileGridLayout = newTabPageLayout.findViewById(R.id.tile_grid_layout);
-        Assert.assertNotNull("Unable to retrieve the TileGridLayout.", tileGridLayout);
-        return tileGridLayout;
-    }
-
-    private View getTileViewFor(SiteSuggestion suggestion) {
-        View tileView = getTileGridLayout().getTileView(suggestion);
-        Assert.assertNotNull("Tile not found for suggestion " + suggestion.url, tileView);
-
-        return tileView;
-    }
-
-    private void invokeContextMenu(View view, int contextMenuItemId) throws ExecutionException {
-        TestTouchUtils.performLongClickOnMainSync(
-                InstrumentationRegistry.getInstrumentation(), view);
-        Assert.assertTrue(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction(
-                mActivityTestRule.getActivity(), contextMenuItemId, 0));
-    }
-
-    /** Wait for the snackbar associated to a tile dismissal to be shown and returns its button. */
-    private static View waitForSnackbar(final ChromeActivity activity) {
-        final String expectedSnackbarMessage =
-                activity.getResources().getString(R.string.most_visited_item_removed);
-        CriteriaHelper.pollUiThread(new Criteria("The snackbar was not shown.") {
-            @Override
-            public boolean isSatisfied() {
-                SnackbarManager snackbarManager = activity.getSnackbarManager();
-                if (!snackbarManager.isShowing()) return false;
-
-                TextView snackbarMessage = (TextView) activity.findViewById(R.id.snackbar_message);
-                if (snackbarMessage == null) return false;
-
-                return snackbarMessage.getText().toString().equals(expectedSnackbarMessage);
-            }
-        });
-
-        return activity.findViewById(R.id.snackbar_button);
-    }
-
-    private void waitForTileRemoved(final SiteSuggestion suggestion)
-            throws TimeoutException, InterruptedException {
-        TileGridLayout tileContainer = getTileGridLayout();
-        final SuggestionsTileView removedTile = tileContainer.getTileView(suggestion);
-        if (removedTile == null) return;
-
-        final CallbackHelper callback = new CallbackHelper();
-        tileContainer.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
-            @Override
-            public void onChildViewAdded(View parent, View child) {}
-
-            @Override
-            public void onChildViewRemoved(View parent, View child) {
-                if (child == removedTile) callback.notifyCalled();
-            }
-        });
-        callback.waitForCallback("The expected tile was not removed.", 0);
-        tileContainer.setOnHierarchyChangeListener(null);
-    }
-
-    private void waitForTileAdded(final SiteSuggestion suggestion)
-            throws TimeoutException, InterruptedException {
-        TileGridLayout tileContainer = getTileGridLayout();
-        if (tileContainer.getTileView(suggestion) != null) return;
-
-        final CallbackHelper callback = new CallbackHelper();
-        tileContainer.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
-            @Override
-            public void onChildViewAdded(View parent, View child) {
-                if (!(child instanceof SuggestionsTileView)) return;
-                if (!((SuggestionsTileView) child).getData().equals(suggestion)) return;
-
-                callback.notifyCalled();
-            }
-
-            @Override
-            public void onChildViewRemoved(View parent, View child) {}
-        });
-        callback.waitForCallback("The expected tile was not added.", 0);
-        tileContainer.setOnHierarchyChangeListener(null);
-    }
-
-    private SiteSuggestion recreateSuggestionsWithExploreTile() {
-        // need a copy of the list in order to modify it.
-        final ArrayList<SiteSuggestion> currentSuggestions =
-                new ArrayList<>(mMostVisitedSites.getCurrentSites());
-
-        SiteSuggestion exploreTile = new SiteSuggestion("chrome-native://explore",
-                "chrome-native://explore", "", TileTitleSource.UNKNOWN, TileSource.EXPLORE,
-                TileSectionType.PERSONALIZED, new Date());
-        currentSuggestions.add(exploreTile);
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mMostVisitedSites.setTileSuggestions(currentSuggestions));
-
-        return exploreTile;
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
new file mode 100644
index 0000000..81ba1615
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
@@ -0,0 +1,335 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+
+import static org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites.createSiteSuggestion;
+
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.support.annotation.Nullable;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.test.filters.MediumTest;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.RetryOnFailure;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeApplication;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.ntp.NewTabPage;
+import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
+import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
+import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.util.ViewUtils;
+import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.NewTabPageTestUtils;
+import org.chromium.chrome.test.util.RenderTestRule;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge;
+import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
+import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.net.test.EmbeddedTestServerRule;
+import org.chromium.ui.modelutil.ListObservable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Instrumentation tests for the {@link TileGridLayout} on the New Tab Page.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
+public class TileGridLayoutTest {
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    @Rule
+    public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule();
+
+    @Rule
+    public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule();
+
+    @Rule
+    public RenderTestRule mRenderTestRule = new RenderTestRule();
+
+    private static final String[] FAKE_MOST_VISITED_URLS = new String[] {
+            "/chrome/test/data/android/navigate/one.html",
+            "/chrome/test/data/android/navigate/two.html",
+            "/chrome/test/data/android/navigate/three.html",
+            "/chrome/test/data/android/navigate/four.html",
+            "/chrome/test/data/android/navigate/five.html",
+            "/chrome/test/data/android/navigate/six.html",
+            "/chrome/test/data/android/navigate/seven.html",
+            "/chrome/test/data/android/navigate/eight.html",
+            "/chrome/test/data/android/navigate/nine.html",
+    };
+
+    private static final String[] FAKE_MOST_VISITED_TITLES =
+            new String[] {"ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"};
+
+    private final CallbackHelper mLoadCompleteHelper = new CallbackHelper();
+
+    @Test
+    @MediumTest
+    @Feature({"NewTabPage", "RenderTest"})
+    // TODO(https://crbug.com/906151): Add new goldens and enable ExploreSites.
+    @DisableFeatures(ChromeFeatureList.EXPLORE_SITES)
+    public void testTileGridAppearance() throws Exception {
+        NewTabPage ntp = setUpFakeDataToShowOnNtp(FAKE_MOST_VISITED_URLS.length);
+        mRenderTestRule.render(getTileGridLayout(ntp), "ntp_tile_grid_layout");
+    }
+
+    @Test
+    //@MediumTest
+    @DisabledTest(message = "crbug.com/771648")
+    @Feature({"NewTabPage", "RenderTest"})
+    public void testModernTileGridAppearance_Full() throws IOException, InterruptedException {
+        View tileGridLayout = renderTiles(makeSuggestions(FAKE_MOST_VISITED_URLS.length));
+
+        setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, mActivityTestRule.getActivity());
+        mRenderTestRule.render(tileGridLayout, "modern_full_grid_portrait");
+
+        setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, mActivityTestRule.getActivity());
+        mRenderTestRule.render(tileGridLayout, "modern_full_grid_landscape");
+
+        // In landscape, modern tiles should use all available space.
+        int tileGridMaxWidthPx = tileGridLayout.getResources().getDimensionPixelSize(
+                R.dimen.tile_grid_layout_max_width);
+        if (((FrameLayout) tileGridLayout.getParent()).getMeasuredWidth() > tileGridMaxWidthPx) {
+            assertThat(tileGridLayout.getMeasuredWidth(), greaterThan(tileGridMaxWidthPx));
+        }
+    }
+
+    @Test
+    //@MediumTest
+    @DisabledTest(message = "crbug.com/771648")
+    @RetryOnFailure
+    @Feature({"NewTabPage", "RenderTest"})
+    public void testModernTileGridAppearance_Two() throws IOException, InterruptedException {
+        View tileGridLayout = renderTiles(makeSuggestions(2));
+
+        setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, mActivityTestRule.getActivity());
+        mRenderTestRule.render(tileGridLayout, "modern_two_tiles_grid_portrait");
+
+        setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, mActivityTestRule.getActivity());
+        mRenderTestRule.render(tileGridLayout, "modern_two_tiles_grid_landscape");
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"NewTabPage", "RenderTest"})
+    // TODO(https://crbug.com/906151): Add new goldens and enable ExploreSites.
+    @DisableFeatures(ChromeFeatureList.EXPLORE_SITES)
+    public void testTileAppearanceModern()
+            throws IOException, InterruptedException, TimeoutException {
+        List<SiteSuggestion> suggestions = makeSuggestions(2);
+        List<String> offlineAvailableUrls = Collections.singletonList(suggestions.get(0).url);
+        ViewGroup tiles = renderTiles(suggestions, offlineAvailableUrls);
+
+        mLoadCompleteHelper.waitForCallback(0);
+
+        mRenderTestRule.render(tiles.getChildAt(0), "tile_modern_offline");
+        mRenderTestRule.render(tiles.getChildAt(1), "tile_modern");
+    }
+
+    private List<SiteSuggestion> makeSuggestions(int count) {
+        List<SiteSuggestion> siteSuggestions = new ArrayList<>(count);
+
+        assertEquals(FAKE_MOST_VISITED_URLS.length, FAKE_MOST_VISITED_TITLES.length);
+        assertTrue(count <= FAKE_MOST_VISITED_URLS.length);
+
+        for (int i = 0; i < count; i++) {
+            String url = mTestServerRule.getServer().getURL(FAKE_MOST_VISITED_URLS[i]);
+            siteSuggestions.add(createSiteSuggestion(FAKE_MOST_VISITED_TITLES[i], url));
+        }
+
+        return siteSuggestions;
+    }
+
+    private NewTabPage setUpFakeDataToShowOnNtp(int suggestionCount) throws InterruptedException {
+        List<SiteSuggestion> siteSuggestions = makeSuggestions(suggestionCount);
+
+        FakeMostVisitedSites mMostVisitedSites = new FakeMostVisitedSites();
+        mMostVisitedSites.setTileSuggestions(siteSuggestions);
+        mSuggestionsDeps.getFactory().mostVisitedSites = mMostVisitedSites;
+
+        mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource();
+
+        mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
+
+        Tab mTab = mActivityTestRule.getActivity().getActivityTab();
+        NewTabPageTestUtils.waitForNtpLoaded(mTab);
+
+        assertTrue(mTab.getNativePage() instanceof NewTabPage);
+        NewTabPage ntp = (NewTabPage) mTab.getNativePage();
+
+        org.chromium.chrome.test.util.ViewUtils.waitForView(
+                (ViewGroup) ntp.getView(), ViewMatchers.withId(R.id.tile_grid_layout));
+
+        return ntp;
+    }
+
+    private void setOrientation(final int requestedOrientation, final Activity activity) {
+        if (orientationMatchesRequest(activity, requestedOrientation)) return;
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> activity.setRequestedOrientation(requestedOrientation));
+
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return orientationMatchesRequest(activity, requestedOrientation);
+            }
+        });
+    }
+
+    /**
+     * Checks whether the requested orientation matches the current one.
+     * @param activity Activity to check the orientation from. We pull its {@link Configuration} and
+     *         content {@link View}.
+     * @param requestedOrientation The requested orientation, as used in
+     *         {@link ActivityInfo#screenOrientation}.
+     */
+    private boolean orientationMatchesRequest(Activity activity, int requestedOrientation) {
+        // Note: Requests use a constant from ActivityInfo, not Configuration.ORIENTATION_*!
+        boolean expectLandscape = requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+
+        // We check the orientation by looking at the dimensions of the content view. Looking at
+        // orientation from the configuration is not reliable as sometimes the activity gets the
+        // event that its configuration changed, but has not updated its layout yet.
+        Configuration configuration = activity.getResources().getConfiguration();
+        View contentView = activity.findViewById(android.R.id.content);
+        int smallestWidthPx = ViewUtils.dpToPx(activity, configuration.smallestScreenWidthDp);
+        boolean viewIsLandscape = contentView.getMeasuredWidth() > smallestWidthPx;
+
+        return expectLandscape == viewIsLandscape;
+    }
+
+    private TileGridLayout getTileGridLayout(NewTabPage ntp) {
+        TileGridLayout tileGridLayout = ntp.getView().findViewById(R.id.tile_grid_layout);
+        assertNotNull("Unable to retrieve the TileGridLayout.", tileGridLayout);
+        return tileGridLayout;
+    }
+
+    /**
+     * Starts and sets up an activity to render the provided site suggestions in the activity.
+     * @return the layout in which the suggestions are rendered.
+     */
+    private TileGridLayout renderTiles(List<SiteSuggestion> siteSuggestions,
+            List<String> offlineUrls) throws IOException, InterruptedException {
+        // Launching the activity, that should now use the right UI.
+        mActivityTestRule.startMainActivityOnBlankPage();
+        ChromeActivity activity = mActivityTestRule.getActivity();
+
+        // Setting up the dummy data.
+        FakeMostVisitedSites mostVisitedSites = new FakeMostVisitedSites();
+        mostVisitedSites.setTileSuggestions(siteSuggestions);
+        mSuggestionsDeps.getFactory().mostVisitedSites = mostVisitedSites;
+        mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource();
+
+        FrameLayout contentView = new FrameLayout(activity);
+        UiConfig uiConfig = new UiConfig(contentView);
+
+        return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            activity.setContentView(contentView);
+
+            SiteSectionViewHolder viewHolder = SiteSection.createViewHolder(
+                    SiteSection.inflateSiteSection(contentView), uiConfig);
+
+            uiConfig.updateDisplayStyle();
+
+            SiteSection siteSection = createSiteSection(viewHolder, uiConfig, offlineUrls);
+            siteSection.getTileGroupForTesting().onSwitchToForeground(false);
+            assertTrue("Tile Data should be visible.", siteSection.isVisible());
+
+            siteSection.onBindViewHolder(viewHolder, 0);
+            contentView.addView(viewHolder.itemView);
+
+            return (TileGridLayout) viewHolder.itemView;
+        });
+    }
+
+    private TileGridLayout renderTiles(List<SiteSuggestion> siteSuggestions)
+            throws IOException, InterruptedException {
+        return renderTiles(siteSuggestions, Collections.emptyList());
+    }
+
+    private SiteSection createSiteSection(
+            final SiteSectionViewHolder viewHolder, UiConfig uiConfig, List<String> offlineUrls) {
+        ThreadUtils.assertOnUiThread();
+
+        ChromeActivity activity = mActivityTestRule.getActivity();
+
+        Profile profile = Profile.getLastUsedProfile();
+        SuggestionsUiDelegate uiDelegate = new SuggestionsUiDelegateImpl(
+                mSuggestionsDeps.getFactory().createSuggestionSource(null),
+                mSuggestionsDeps.getFactory().createEventReporter(), null, profile, null,
+                ChromeApplication.getReferencePool(), activity.getSnackbarManager());
+
+        FakeOfflinePageBridge offlinePageBridge = new FakeOfflinePageBridge();
+        List<OfflinePageItem> offlinePageItems = new ArrayList<>();
+        for (int i = 0; i < offlineUrls.size(); i++) {
+            offlinePageItems.add(
+                    FakeOfflinePageBridge.createOfflinePageItem(offlineUrls.get(i), i + 1L));
+        }
+        offlinePageBridge.setItems(offlinePageItems);
+        offlinePageBridge.setIsOfflinePageModelLoaded(true);
+
+        TileGroup.Delegate delegate = new TileGroupDelegateImpl(activity, profile, null, null) {
+            @Override
+            public void onLoadingComplete(List<Tile> tiles) {
+                super.onLoadingComplete(tiles);
+                mLoadCompleteHelper.notifyCalled();
+            }
+        };
+
+        SiteSection siteSection =
+                new SiteSection(uiDelegate, null, delegate, offlinePageBridge, uiConfig);
+
+        siteSection.addObserver(new ListObservable.ListObserver<PartialBindCallback>() {
+            @Override
+            public void onItemRangeChanged(ListObservable child, int index, int count,
+                    @Nullable PartialBindCallback payload) {
+                if (payload != null) payload.onResult(viewHolder);
+            }
+        });
+
+        return siteSection;
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupTest.java
new file mode 100644
index 0000000..22d3713
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupTest.java
@@ -0,0 +1,279 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.test.filters.MediumTest;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.RetryOnFailure;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.native_page.ContextMenuManager;
+import org.chromium.chrome.browser.ntp.NewTabPage;
+import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView;
+import org.chromium.chrome.browser.snackbar.SnackbarManager;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.NewTabPageTestUtils;
+import org.chromium.chrome.test.util.ViewUtils;
+import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
+import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.content_public.browser.test.util.TestTouchUtils;
+import org.chromium.net.test.EmbeddedTestServer;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Instrumentation tests for {@link TileGroup} on the New Tab Page.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@RetryOnFailure
+public class TileGroupTest {
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    @Rule
+    public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule();
+
+    private static final String[] FAKE_MOST_VISITED_URLS =
+            new String[] {"/chrome/test/data/android/navigate/one.html",
+                    "/chrome/test/data/android/navigate/two.html",
+                    "/chrome/test/data/android/navigate/three.html"};
+
+    private NewTabPage mNtp;
+    private String[] mSiteSuggestionUrls;
+    private FakeMostVisitedSites mMostVisitedSites;
+    private EmbeddedTestServer mTestServer;
+
+    @Before
+    public void setUp() throws Exception {
+        mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
+
+        mSiteSuggestionUrls = mTestServer.getURLs(FAKE_MOST_VISITED_URLS);
+
+        mMostVisitedSites = new FakeMostVisitedSites();
+        mSuggestionsDeps.getFactory().mostVisitedSites = mMostVisitedSites;
+        mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls);
+
+        mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource();
+        initializeTab();
+    }
+
+    public void initializeTab() throws Exception {
+        mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL);
+        Tab mTab = mActivityTestRule.getActivity().getActivityTab();
+        NewTabPageTestUtils.waitForNtpLoaded(mTab);
+
+        Assert.assertTrue(mTab.getNativePage() instanceof NewTabPage);
+        mNtp = (NewTabPage) mTab.getNativePage();
+
+        ViewUtils.waitForView(
+                (ViewGroup) mNtp.getView(), ViewMatchers.withId(R.id.tile_grid_layout));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTestServer.stopAndDestroyServer();
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"NewTabPage"})
+    public void testDismissTileWithContextMenu() throws Exception {
+        SiteSuggestion siteToDismiss = mMostVisitedSites.getCurrentSites().get(0);
+        final View tileView = getTileViewFor(siteToDismiss);
+
+        // Dismiss the tile using the context menu.
+        invokeContextMenu(tileView, ContextMenuManager.ContextMenuItemId.REMOVE);
+        Assert.assertTrue(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0]));
+
+        // Ensure that the removal is reflected in the ui.
+        Assert.assertEquals(3, getTileGridLayout().getChildCount());
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls[1], mSiteSuggestionUrls[2]);
+        });
+        waitForTileRemoved(siteToDismiss);
+        Assert.assertEquals(2, getTileGridLayout().getChildCount());
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"NewTabPage"})
+    public void testDismissExploreTileWithContextMenuFails() throws Exception {
+        SiteSuggestion exploreTile = recreateSuggestionsWithExploreTile();
+        initializeTab();
+
+        Assert.assertEquals(4, getTileGridLayout().getChildCount());
+
+        final View tileView = getTileViewFor(exploreTile);
+        TestTouchUtils.performLongClickOnMainSync(
+                InstrumentationRegistry.getInstrumentation(), tileView);
+        Assert.assertFalse(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction(
+                mActivityTestRule.getActivity(), ContextMenuManager.ContextMenuItemId.REMOVE, 0));
+        Assert.assertEquals(4, getTileGridLayout().getChildCount());
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"NewTabPage"})
+    public void testDismissTileUndo() throws Exception {
+        SiteSuggestion siteToDismiss = mMostVisitedSites.getCurrentSites().get(0);
+        final ViewGroup tileContainer = getTileGridLayout();
+        final View tileView = getTileViewFor(siteToDismiss);
+        Assert.assertEquals(3, tileContainer.getChildCount());
+
+        // Dismiss the tile using the context menu.
+        invokeContextMenu(tileView, ContextMenuManager.ContextMenuItemId.REMOVE);
+
+        // Ensure that the removal update goes through.
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls[1], mSiteSuggestionUrls[2]);
+        });
+        waitForTileRemoved(siteToDismiss);
+        Assert.assertEquals(2, tileContainer.getChildCount());
+        final View snackbarButton = waitForSnackbar(mActivityTestRule.getActivity());
+
+        Assert.assertTrue(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0]));
+        TestThreadUtils.runOnUiThreadBlocking(() -> { snackbarButton.callOnClick(); });
+
+        Assert.assertFalse(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0]));
+
+        // Ensure that the removal of the update goes through.
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls); });
+        waitForTileAdded(siteToDismiss);
+        Assert.assertEquals(3, tileContainer.getChildCount());
+    }
+
+    private NewTabPageRecyclerView getRecyclerView() {
+        return mNtp.getNewTabPageView().getRecyclerView();
+    }
+
+    private TileGridLayout getTileGridLayout() {
+        ViewGroup newTabPageLayout = mNtp.getNewTabPageLayout();
+        Assert.assertNotNull("Unable to retrieve the NewTabPageLayout.", newTabPageLayout);
+
+        TileGridLayout tileGridLayout = newTabPageLayout.findViewById(R.id.tile_grid_layout);
+        Assert.assertNotNull("Unable to retrieve the TileGridLayout.", tileGridLayout);
+        return tileGridLayout;
+    }
+
+    private View getTileViewFor(SiteSuggestion suggestion) {
+        View tileView = getTileGridLayout().getTileView(suggestion);
+        Assert.assertNotNull("Tile not found for suggestion " + suggestion.url, tileView);
+
+        return tileView;
+    }
+
+    private void invokeContextMenu(View view, int contextMenuItemId) throws ExecutionException {
+        TestTouchUtils.performLongClickOnMainSync(
+                InstrumentationRegistry.getInstrumentation(), view);
+        Assert.assertTrue(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction(
+                mActivityTestRule.getActivity(), contextMenuItemId, 0));
+    }
+
+    /** Wait for the snackbar associated to a tile dismissal to be shown and returns its button. */
+    private static View waitForSnackbar(final ChromeActivity activity) {
+        final String expectedSnackbarMessage =
+                activity.getResources().getString(R.string.most_visited_item_removed);
+        CriteriaHelper.pollUiThread(new Criteria("The snackbar was not shown.") {
+            @Override
+            public boolean isSatisfied() {
+                SnackbarManager snackbarManager = activity.getSnackbarManager();
+                if (!snackbarManager.isShowing()) return false;
+
+                TextView snackbarMessage = (TextView) activity.findViewById(R.id.snackbar_message);
+                if (snackbarMessage == null) return false;
+
+                return snackbarMessage.getText().toString().equals(expectedSnackbarMessage);
+            }
+        });
+
+        return activity.findViewById(R.id.snackbar_button);
+    }
+
+    private void waitForTileRemoved(final SiteSuggestion suggestion)
+            throws TimeoutException, InterruptedException {
+        TileGridLayout tileContainer = getTileGridLayout();
+        final SuggestionsTileView removedTile = tileContainer.getTileView(suggestion);
+        if (removedTile == null) return;
+
+        final CallbackHelper callback = new CallbackHelper();
+        tileContainer.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
+            @Override
+            public void onChildViewAdded(View parent, View child) {}
+
+            @Override
+            public void onChildViewRemoved(View parent, View child) {
+                if (child == removedTile) callback.notifyCalled();
+            }
+        });
+        callback.waitForCallback("The expected tile was not removed.", 0);
+        tileContainer.setOnHierarchyChangeListener(null);
+    }
+
+    private void waitForTileAdded(final SiteSuggestion suggestion)
+            throws TimeoutException, InterruptedException {
+        TileGridLayout tileContainer = getTileGridLayout();
+        if (tileContainer.getTileView(suggestion) != null) return;
+
+        final CallbackHelper callback = new CallbackHelper();
+        tileContainer.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
+            @Override
+            public void onChildViewAdded(View parent, View child) {
+                if (!(child instanceof SuggestionsTileView)) return;
+                if (!((SuggestionsTileView) child).getData().equals(suggestion)) return;
+
+                callback.notifyCalled();
+            }
+
+            @Override
+            public void onChildViewRemoved(View parent, View child) {}
+        });
+        callback.waitForCallback("The expected tile was not added.", 0);
+        tileContainer.setOnHierarchyChangeListener(null);
+    }
+
+    private SiteSuggestion recreateSuggestionsWithExploreTile() {
+        // need a copy of the list in order to modify it.
+        final ArrayList<SiteSuggestion> currentSuggestions =
+                new ArrayList<>(mMostVisitedSites.getCurrentSites());
+
+        SiteSuggestion exploreTile = new SiteSuggestion("chrome-native://explore",
+                "chrome-native://explore", "", TileTitleSource.UNKNOWN, TileSource.EXPLORE,
+                TileSectionType.PERSONALIZED, new Date());
+        currentSuggestions.add(exploreTile);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> mMostVisitedSites.setTileSuggestions(currentSuggestions));
+
+        return exploreTile;
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java
deleted file mode 100644
index a6b737d..0000000
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java
+++ /dev/null
@@ -1,505 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.suggestions;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import static org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites.createSiteSuggestion;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import org.hamcrest.CoreMatchers;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.favicon.IconType;
-import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
-import org.chromium.chrome.browser.native_page.ContextMenuManager;
-import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters;
-import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
-import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle;
-import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Unit tests for {@link TileGroup}.
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TileGroupUnitTest {
-    private static final int MAX_TILES_TO_FETCH = 4;
-    private static final int TILE_TITLE_LINES = 1;
-    private static final String[] URLS = {"https://www.google.com", "https://tellmedadjokes.com"};
-
-    @Rule
-    public TestRule mFeaturesProcessor = new Features.JUnitProcessor();
-
-    @Mock
-    private TileGroup.Observer mTileGroupObserver;
-    @Mock
-    private TileGroup.Delegate mTileGroupDelegate;
-
-    private FakeMostVisitedSites mMostVisitedSites;
-    private FakeImageFetcher mImageFetcher;
-    private TileRenderer mTileRenderer;
-
-    @Before
-    public void setUp() {
-        CardsVariationParameters.setTestVariationParams(new HashMap<>());
-        MockitoAnnotations.initMocks(this);
-
-        mImageFetcher = new FakeImageFetcher();
-        mTileRenderer = new TileRenderer(
-                RuntimeEnvironment.application, TileStyle.MODERN, TILE_TITLE_LINES, mImageFetcher);
-        mMostVisitedSites = new FakeMostVisitedSites();
-
-        doAnswer(invocation -> {
-            mMostVisitedSites.setObserver(
-                    invocation.getArgument(0), invocation.<Integer>getArgument(1));
-            return null;
-        })
-                .when(mTileGroupDelegate)
-                .setMostVisitedSitesObserver(any(MostVisitedSites.Observer.class), anyInt());
-    }
-
-    @After
-    public void tearDown() {
-        CardsVariationParameters.setTestVariationParams(null);
-    }
-
-    @Test
-    public void testInitialiseWithTileList() {
-        mMostVisitedSites.setTileSuggestions(URLS);
-
-        TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class),
-                mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
-                mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-
-        verify(mTileGroupObserver).onTileCountChanged();
-        verify(mTileGroupObserver).onTileDataChanged();
-
-        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
-        assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
-        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.FETCH_DATA));
-    }
-
-    /**
-     * Test the special case during initialisation, where we notify TileGroup.Observer of changes
-     * event though the data did not change (still empty just like before initialisation).
-     */
-    @Test
-    public void testInitialiseWithEmptyTileList() {
-        TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class),
-                mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
-                mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-
-        verify(mTileGroupObserver).onTileCountChanged();
-        verify(mTileGroupObserver).onTileDataChanged();
-
-        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
-        assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
-        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.FETCH_DATA));
-    }
-
-    @Test
-    public void testReceiveNewTilesWithoutChanges() {
-        TileGroup tileGroup = initialiseTileGroup(URLS);
-
-        // Notify the same thing. No changes so|mTileGroupObserver| should not be notified.
-        mMostVisitedSites.setTileSuggestions(URLS);
-
-        verifyNoMoreInteractions(mTileGroupObserver);
-        verifyNoMoreInteractions(mTileGroupDelegate);
-        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
-    }
-
-    @Test
-    public void testReceiveNewTilesWithoutChanges_TrackLoad() {
-        TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true, URLS);
-
-        // Notify the same thing. No changes so|mTileGroupObserver| should not be notified.
-        mMostVisitedSites.setTileSuggestions(URLS);
-        tileGroup.onSwitchToForeground(/* trackLoadTask: */ true);
-
-        verifyNoMoreInteractions(mTileGroupObserver);
-        verifyNoMoreInteractions(mTileGroupDelegate);
-        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
-    }
-
-    @Test
-    public void testReceiveNewTilesWithDataChanges() {
-        TileGroup tileGroup = initialiseTileGroup(URLS);
-
-        // Notify the about different URLs, but the same number. #onTileCountChanged() should not be
-        // called.
-        mMostVisitedSites.setTileSuggestions("foo", "bar");
-
-        verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2.
-        verify(mTileGroupObserver).onTileDataChanged(); // Data DID change.
-
-        // No load task the second time.
-        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
-        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
-    }
-
-    @Test
-    public void testReceiveNewTilesWithDataChanges_TrackLoad() {
-        TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true, URLS);
-
-        // Notify the about different URLs, but the same number. #onTileCountChanged() should not be
-        // called.
-        mMostVisitedSites.setTileSuggestions("foo", "bar");
-        tileGroup.onSwitchToForeground(/* trackLoadTask: */ true);
-
-        verify(mTileGroupObserver).onTileDataChanged(); // Now data DID change.
-        verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2.
-
-        // We should now have a pending task.
-        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
-        assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
-    }
-
-    @Test
-    public void testReceiveNewTilesWithCountChanges() {
-        TileGroup tileGroup = initialiseTileGroup(URLS);
-
-        mMostVisitedSites.setTileSuggestions(URLS[0]);
-
-        verify(mTileGroupObserver).onTileCountChanged(); // Tile count DID change.
-        verify(mTileGroupObserver).onTileDataChanged(); // Data DID change.
-        verify(mTileGroupDelegate, never())
-                .onLoadingComplete(any()); // No load task the second time.
-        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
-    }
-
-    @Test
-    public void testTileLoadingWhenVisibleNotBlockedForInit() {
-        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
-        when(uiDelegate.isVisible()).thenReturn(true);
-        TileGroup tileGroup =
-                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
-                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-
-        mMostVisitedSites.setTileSuggestions(URLS);
-
-        // Because it's the first load, we accept the incoming tiles and refresh the view.
-        verify(mTileGroupObserver).onTileDataChanged();
-    }
-
-    @Test
-    public void testTileLoadingWhenVisibleBlocked() {
-        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
-        when(uiDelegate.isVisible()).thenReturn(true);
-        TileGroup tileGroup =
-                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
-                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-
-        mMostVisitedSites.setTileSuggestions(URLS);
-        reset(mTileGroupObserver);
-
-        mMostVisitedSites.setTileSuggestions(URLS[0]);
-
-        // Even though the data changed, the notification should not happen because we want to not
-        // show changes to UI elements currently visible
-        verify(mTileGroupObserver, never()).onTileDataChanged();
-
-        // Simulating a switch from background to foreground should force the tilegrid to load the
-        // new data.
-        tileGroup.onSwitchToForeground(true);
-        verify(mTileGroupObserver).onTileDataChanged();
-    }
-
-    @Test
-    public void testTileLoadingWhenVisibleBlocked_2() {
-        TileGroup tileGroup = initialiseTileGroup(true, URLS);
-
-        mMostVisitedSites.setTileSuggestions(URLS[0]);
-
-        // Even though the data changed, the notification should not happen because we want to not
-        // show changes to UI elements currently visible
-        verify(mTileGroupObserver, never()).onTileDataChanged();
-
-        // Simulating a switch from background to foreground should force the tilegrid to load the
-        // new data.
-        tileGroup.onSwitchToForeground(true);
-        verify(mTileGroupObserver).onTileDataChanged();
-    }
-
-    @Test
-    public void testRenderTileView() {
-        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
-        when(uiDelegate.getImageFetcher()).thenReturn(mImageFetcher);
-        TileGroup tileGroup =
-                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
-                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-
-        TileGridViewHolder tileGrid = setupView(tileGroup);
-        TileGridLayout layout = (TileGridLayout) tileGrid.itemView;
-
-        // Initialise the internal list of tiles
-        mMostVisitedSites.setTileSuggestions(URLS);
-
-        // Render them to the layout.
-        tileGrid.refreshData();
-        assertThat(layout.getChildCount(), is(2));
-        assertThat(((SuggestionsTileView) layout.getChildAt(0)).getUrl(), is(URLS[0]));
-        assertThat(((SuggestionsTileView) layout.getChildAt(1)).getUrl(), is(URLS[1]));
-    }
-
-    /** Check for https://crbug.com/703628: don't crash on duplicated URLs. */
-    @Test
-    public void testRenderTileViewWithDuplicatedUrl() {
-        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
-        when(uiDelegate.getImageFetcher()).thenReturn(mock(ImageFetcher.class));
-        TileGroup tileGroup =
-                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
-                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-        TileGridViewHolder tileGrid = setupView(tileGroup);
-
-        // Initialise the internal list of tiles
-        mMostVisitedSites.setTileSuggestions(URLS[0], URLS[1], URLS[0]);
-
-        // Render them to the layout. The duplicated URL should not trigger an exception.
-        tileGrid.refreshData();
-    }
-
-    @Test
-    public void testRenderTileViewReplacing() {
-        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
-        when(uiDelegate.getImageFetcher()).thenReturn(mock(ImageFetcher.class));
-        TileGroup tileGroup =
-                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
-                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-        mMostVisitedSites.setTileSuggestions(URLS);
-
-        // Initialise the layout with views whose URLs don't match the ones of the new tiles.
-        TileGridViewHolder tileGrid = setupView(tileGroup);
-        TileGridLayout layout = (TileGridLayout) tileGrid.itemView;
-        SuggestionsTileView view1 = mock(SuggestionsTileView.class);
-        layout.addView(view1);
-
-        SuggestionsTileView view2 = mock(SuggestionsTileView.class);
-        layout.addView(view2);
-
-        // The tiles should be updated, the old ones removed.
-        tileGrid.refreshData();
-        assertThat(layout.getChildCount(), is(2));
-        assertThat(layout.indexOfChild(view1), is(-1));
-        assertThat(layout.indexOfChild(view2), is(-1));
-    }
-
-    @Test
-    public void testRenderTileViewRecycling() {
-        mMostVisitedSites.setTileSuggestions(URLS);
-        List<SiteSuggestion> sites = mMostVisitedSites.getCurrentSites();
-        TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class),
-                mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
-                mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-
-        // Initialise the layout with views whose URLs match the ones of the new tiles.
-        TileGridLayout layout = new TileGridLayout(RuntimeEnvironment.application, null);
-        SuggestionsTileView view1 = mock(SuggestionsTileView.class);
-        when(view1.getData()).thenReturn(sites.get(0));
-        layout.addView(view1);
-
-        SuggestionsTileView view2 = mock(SuggestionsTileView.class);
-        when(view2.getData()).thenReturn(sites.get(1));
-        layout.addView(view2);
-
-        // The tiles should be updated, the old ones reused.
-        setupView(tileGroup).refreshData();
-        assertThat(layout.getChildCount(), is(2));
-        assertThat(layout.getChildAt(0), CoreMatchers.is(view1));
-        assertThat(layout.getChildAt(1), CoreMatchers.is(view2));
-    }
-
-    @Test
-    public void testIconLoadingForInit() {
-        TileGroup tileGroup = initialiseTileGroup(URLS);
-        Tile tile = tileGroup.getTileSections().get(TileSectionType.PERSONALIZED).get(0);
-
-        // Loading complete should be delayed until the icons are done loading.
-        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
-
-        mImageFetcher.fulfillLargeIconRequests();
-
-        // The load should now be complete.
-        verify(mTileGroupDelegate).onLoadingComplete(any());
-        verify(mTileGroupObserver).onTileIconChanged(eq(tile));
-        verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class));
-    }
-
-    @Test
-    public void testIconLoadingWhenTileNotRegistered() {
-        TileGroup tileGroup = initialiseTileGroup();
-        Tile tile = new Tile(createSiteSuggestion("title", URLS[0]), 0);
-
-        ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null);
-        mTileRenderer.buildTileView(tile, layout, tileGroup.getTileSetupDelegate());
-
-        // Ensure we run the callback for the new tile.
-        assertEquals(1, mImageFetcher.getPendingIconCallbackCount());
-        mImageFetcher.fulfillLargeIconRequests();
-
-        verify(mTileGroupObserver, never()).onTileIconChanged(tile);
-    }
-
-    private TileGridViewHolder setupView(TileGroup tileGroup) {
-        TileGridLayout layout = new TileGridLayout(RuntimeEnvironment.application, null);
-        TileGridViewHolder tileGrid = new TileGridViewHolder(layout, 4, 2, mock(UiConfig.class));
-        tileGrid.bindDataSource(tileGroup, mTileRenderer);
-        return tileGrid;
-    }
-
-    @Test
-    public void testIconLoading_Sync() {
-        TileGroup tileGroup = initialiseTileGroup();
-        mImageFetcher.fulfillLargeIconRequests();
-        reset(mTileGroupObserver, mTileGroupDelegate);
-
-        // Notify for a second set.
-        mMostVisitedSites.setTileSuggestions(URLS);
-        setupView(tileGroup).refreshData();
-        mImageFetcher.fulfillLargeIconRequests();
-
-        // Data changed but no loading complete event is sent
-        verify(mTileGroupObserver).onTileDataChanged();
-        verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class));
-        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
-    }
-
-    @Test
-    public void testIconLoading_AsyncNoTrack() {
-        TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true);
-        mImageFetcher.fulfillLargeIconRequests();
-        reset(mTileGroupObserver, mTileGroupDelegate);
-
-        // Notify for a second set.
-        mMostVisitedSites.setTileSuggestions(URLS);
-        tileGroup.onSwitchToForeground(/* trackLoadTask: */ false);
-        setupView(tileGroup).refreshData();
-        mImageFetcher.fulfillLargeIconRequests();
-
-        // Data changed but no loading complete event is sent (same as sync)
-        verify(mTileGroupObserver).onTileDataChanged();
-        verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class));
-        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
-    }
-
-    @Test
-    public void testIconLoading_AsyncTrack() {
-        TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true);
-        mImageFetcher.fulfillLargeIconRequests();
-        reset(mTileGroupObserver, mTileGroupDelegate);
-
-        // Notify for a second set.
-        mMostVisitedSites.setTileSuggestions(URLS);
-        tileGroup.onSwitchToForeground(/* trackLoadTask: */ true);
-        setupView(tileGroup).refreshData();
-        mImageFetcher.fulfillLargeIconRequests();
-
-        // Data changed but no loading complete event is sent
-        verify(mTileGroupObserver).onTileDataChanged();
-        verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class));
-        verify(mTileGroupDelegate).onLoadingComplete(any());
-    }
-
-    /** {@link #initialiseTileGroup(boolean, String...)} override that does not defer loads. */
-    private TileGroup initialiseTileGroup(String... urls) {
-        return initialiseTileGroup(false, urls);
-    }
-
-    /**
-     * @param deferLoad whether to defer the load until
-     *                  {@link TileGroup#onSwitchToForeground(boolean)} is called. Works by
-     *                  pretending that the UI is visible.
-     * @param urls URLs used to initialise the tile group.
-     */
-    private TileGroup initialiseTileGroup(boolean deferLoad, String... urls) {
-        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
-        when(uiDelegate.getImageFetcher()).thenReturn(mImageFetcher);
-        when(uiDelegate.isVisible()).thenReturn(deferLoad);
-
-        mMostVisitedSites.setTileSuggestions(urls);
-
-        TileGroup tileGroup =
-                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
-                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
-        tileGroup.startObserving(MAX_TILES_TO_FETCH);
-        setupView(tileGroup).refreshData();
-
-        reset(mTileGroupObserver);
-        reset(mTileGroupDelegate);
-        return tileGroup;
-    }
-
-    private class FakeImageFetcher extends ImageFetcher {
-        private final List<LargeIconCallback> mCallbackList = new ArrayList<>();
-
-        public FakeImageFetcher() {
-            super(null, null, null);
-        }
-
-        @Override
-        public void makeLargeIconRequest(String url, int size, LargeIconCallback callback) {
-            mCallbackList.add(callback);
-        }
-
-        public void fulfillLargeIconRequests(Bitmap bitmap, int color, boolean isColorDefault) {
-            for (LargeIconCallback callback : mCallbackList) {
-                callback.onLargeIconAvailable(bitmap, color, isColorDefault, IconType.INVALID);
-            }
-            mCallbackList.clear();
-        }
-
-        public int getPendingIconCallbackCount() {
-            return mCallbackList.size();
-        }
-
-        public void fulfillLargeIconRequests() {
-            fulfillLargeIconRequests(mock(Bitmap.class), Color.BLACK, false);
-        }
-    }
-}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
new file mode 100644
index 0000000..1421f69
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
@@ -0,0 +1,508 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import static org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites.createSiteSuggestion;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.favicon.IconType;
+import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
+import org.chromium.chrome.browser.native_page.ContextMenuManager;
+import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.suggestions.ImageFetcher;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle;
+import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Unit tests for {@link TileGroup}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class TileGroupUnitTest {
+    private static final int MAX_TILES_TO_FETCH = 4;
+    private static final int TILE_TITLE_LINES = 1;
+    private static final String[] URLS = {"https://www.google.com", "https://tellmedadjokes.com"};
+
+    @Rule
+    public TestRule mFeaturesProcessor = new Features.JUnitProcessor();
+
+    @Mock
+    private TileGroup.Observer mTileGroupObserver;
+    @Mock
+    private TileGroup.Delegate mTileGroupDelegate;
+
+    private FakeMostVisitedSites mMostVisitedSites;
+    private FakeImageFetcher mImageFetcher;
+    private TileRenderer mTileRenderer;
+
+    @Before
+    public void setUp() {
+        CardsVariationParameters.setTestVariationParams(new HashMap<>());
+        MockitoAnnotations.initMocks(this);
+
+        mImageFetcher = new FakeImageFetcher();
+        mTileRenderer = new TileRenderer(
+                RuntimeEnvironment.application, TileStyle.MODERN, TILE_TITLE_LINES, mImageFetcher);
+        mMostVisitedSites = new FakeMostVisitedSites();
+
+        doAnswer(invocation -> {
+            mMostVisitedSites.setObserver(
+                    invocation.getArgument(0), invocation.<Integer>getArgument(1));
+            return null;
+        })
+                .when(mTileGroupDelegate)
+                .setMostVisitedSitesObserver(any(MostVisitedSites.Observer.class), anyInt());
+    }
+
+    @After
+    public void tearDown() {
+        CardsVariationParameters.setTestVariationParams(null);
+    }
+
+    @Test
+    public void testInitialiseWithTileList() {
+        mMostVisitedSites.setTileSuggestions(URLS);
+
+        TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class),
+                mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+                mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+        verify(mTileGroupObserver).onTileCountChanged();
+        verify(mTileGroupObserver).onTileDataChanged();
+
+        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
+        assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
+        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.FETCH_DATA));
+    }
+
+    /**
+     * Test the special case during initialisation, where we notify TileGroup.Observer of changes
+     * event though the data did not change (still empty just like before initialisation).
+     */
+    @Test
+    public void testInitialiseWithEmptyTileList() {
+        TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class),
+                mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+                mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+        verify(mTileGroupObserver).onTileCountChanged();
+        verify(mTileGroupObserver).onTileDataChanged();
+
+        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
+        assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
+        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.FETCH_DATA));
+    }
+
+    @Test
+    public void testReceiveNewTilesWithoutChanges() {
+        TileGroup tileGroup = initialiseTileGroup(URLS);
+
+        // Notify the same thing. No changes so|mTileGroupObserver| should not be notified.
+        mMostVisitedSites.setTileSuggestions(URLS);
+
+        verifyNoMoreInteractions(mTileGroupObserver);
+        verifyNoMoreInteractions(mTileGroupDelegate);
+        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
+    }
+
+    @Test
+    public void testReceiveNewTilesWithoutChanges_TrackLoad() {
+        TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true, URLS);
+
+        // Notify the same thing. No changes so|mTileGroupObserver| should not be notified.
+        mMostVisitedSites.setTileSuggestions(URLS);
+        tileGroup.onSwitchToForeground(/* trackLoadTask: */ true);
+
+        verifyNoMoreInteractions(mTileGroupObserver);
+        verifyNoMoreInteractions(mTileGroupDelegate);
+        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
+    }
+
+    @Test
+    public void testReceiveNewTilesWithDataChanges() {
+        TileGroup tileGroup = initialiseTileGroup(URLS);
+
+        // Notify the about different URLs, but the same number. #onTileCountChanged() should not be
+        // called.
+        mMostVisitedSites.setTileSuggestions("foo", "bar");
+
+        verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2.
+        verify(mTileGroupObserver).onTileDataChanged(); // Data DID change.
+
+        // No load task the second time.
+        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
+        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
+    }
+
+    @Test
+    public void testReceiveNewTilesWithDataChanges_TrackLoad() {
+        TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true, URLS);
+
+        // Notify the about different URLs, but the same number. #onTileCountChanged() should not be
+        // called.
+        mMostVisitedSites.setTileSuggestions("foo", "bar");
+        tileGroup.onSwitchToForeground(/* trackLoadTask: */ true);
+
+        verify(mTileGroupObserver).onTileDataChanged(); // Now data DID change.
+        verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2.
+
+        // We should now have a pending task.
+        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
+        assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
+    }
+
+    @Test
+    public void testReceiveNewTilesWithCountChanges() {
+        TileGroup tileGroup = initialiseTileGroup(URLS);
+
+        mMostVisitedSites.setTileSuggestions(URLS[0]);
+
+        verify(mTileGroupObserver).onTileCountChanged(); // Tile count DID change.
+        verify(mTileGroupObserver).onTileDataChanged(); // Data DID change.
+        verify(mTileGroupDelegate, never())
+                .onLoadingComplete(any()); // No load task the second time.
+        assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH));
+    }
+
+    @Test
+    public void testTileLoadingWhenVisibleNotBlockedForInit() {
+        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+        when(uiDelegate.isVisible()).thenReturn(true);
+        TileGroup tileGroup =
+                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
+                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+        mMostVisitedSites.setTileSuggestions(URLS);
+
+        // Because it's the first load, we accept the incoming tiles and refresh the view.
+        verify(mTileGroupObserver).onTileDataChanged();
+    }
+
+    @Test
+    public void testTileLoadingWhenVisibleBlocked() {
+        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+        when(uiDelegate.isVisible()).thenReturn(true);
+        TileGroup tileGroup =
+                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
+                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+        mMostVisitedSites.setTileSuggestions(URLS);
+        reset(mTileGroupObserver);
+
+        mMostVisitedSites.setTileSuggestions(URLS[0]);
+
+        // Even though the data changed, the notification should not happen because we want to not
+        // show changes to UI elements currently visible
+        verify(mTileGroupObserver, never()).onTileDataChanged();
+
+        // Simulating a switch from background to foreground should force the tilegrid to load the
+        // new data.
+        tileGroup.onSwitchToForeground(true);
+        verify(mTileGroupObserver).onTileDataChanged();
+    }
+
+    @Test
+    public void testTileLoadingWhenVisibleBlocked_2() {
+        TileGroup tileGroup = initialiseTileGroup(true, URLS);
+
+        mMostVisitedSites.setTileSuggestions(URLS[0]);
+
+        // Even though the data changed, the notification should not happen because we want to not
+        // show changes to UI elements currently visible
+        verify(mTileGroupObserver, never()).onTileDataChanged();
+
+        // Simulating a switch from background to foreground should force the tilegrid to load the
+        // new data.
+        tileGroup.onSwitchToForeground(true);
+        verify(mTileGroupObserver).onTileDataChanged();
+    }
+
+    @Test
+    public void testRenderTileView() {
+        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+        when(uiDelegate.getImageFetcher()).thenReturn(mImageFetcher);
+        TileGroup tileGroup =
+                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
+                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+        TileGridViewHolder tileGrid = setupView(tileGroup);
+        TileGridLayout layout = (TileGridLayout) tileGrid.itemView;
+
+        // Initialise the internal list of tiles
+        mMostVisitedSites.setTileSuggestions(URLS);
+
+        // Render them to the layout.
+        tileGrid.refreshData();
+        assertThat(layout.getChildCount(), is(2));
+        assertThat(((SuggestionsTileView) layout.getChildAt(0)).getUrl(), is(URLS[0]));
+        assertThat(((SuggestionsTileView) layout.getChildAt(1)).getUrl(), is(URLS[1]));
+    }
+
+    /** Check for https://crbug.com/703628: don't crash on duplicated URLs. */
+    @Test
+    public void testRenderTileViewWithDuplicatedUrl() {
+        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+        when(uiDelegate.getImageFetcher()).thenReturn(mock(ImageFetcher.class));
+        TileGroup tileGroup =
+                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
+                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+        TileGridViewHolder tileGrid = setupView(tileGroup);
+
+        // Initialise the internal list of tiles
+        mMostVisitedSites.setTileSuggestions(URLS[0], URLS[1], URLS[0]);
+
+        // Render them to the layout. The duplicated URL should not trigger an exception.
+        tileGrid.refreshData();
+    }
+
+    @Test
+    public void testRenderTileViewReplacing() {
+        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+        when(uiDelegate.getImageFetcher()).thenReturn(mock(ImageFetcher.class));
+        TileGroup tileGroup =
+                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
+                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+        mMostVisitedSites.setTileSuggestions(URLS);
+
+        // Initialise the layout with views whose URLs don't match the ones of the new tiles.
+        TileGridViewHolder tileGrid = setupView(tileGroup);
+        TileGridLayout layout = (TileGridLayout) tileGrid.itemView;
+        SuggestionsTileView view1 = mock(SuggestionsTileView.class);
+        layout.addView(view1);
+
+        SuggestionsTileView view2 = mock(SuggestionsTileView.class);
+        layout.addView(view2);
+
+        // The tiles should be updated, the old ones removed.
+        tileGrid.refreshData();
+        assertThat(layout.getChildCount(), is(2));
+        assertThat(layout.indexOfChild(view1), is(-1));
+        assertThat(layout.indexOfChild(view2), is(-1));
+    }
+
+    @Test
+    public void testRenderTileViewRecycling() {
+        mMostVisitedSites.setTileSuggestions(URLS);
+        List<SiteSuggestion> sites = mMostVisitedSites.getCurrentSites();
+        TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class),
+                mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+                mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+        // Initialise the layout with views whose URLs match the ones of the new tiles.
+        TileGridLayout layout = new TileGridLayout(RuntimeEnvironment.application, null);
+        SuggestionsTileView view1 = mock(SuggestionsTileView.class);
+        when(view1.getData()).thenReturn(sites.get(0));
+        layout.addView(view1);
+
+        SuggestionsTileView view2 = mock(SuggestionsTileView.class);
+        when(view2.getData()).thenReturn(sites.get(1));
+        layout.addView(view2);
+
+        // The tiles should be updated, the old ones reused.
+        setupView(tileGroup).refreshData();
+        assertThat(layout.getChildCount(), is(2));
+        assertThat(layout.getChildAt(0), CoreMatchers.is(view1));
+        assertThat(layout.getChildAt(1), CoreMatchers.is(view2));
+    }
+
+    @Test
+    public void testIconLoadingForInit() {
+        TileGroup tileGroup = initialiseTileGroup(URLS);
+        Tile tile = tileGroup.getTileSections().get(TileSectionType.PERSONALIZED).get(0);
+
+        // Loading complete should be delayed until the icons are done loading.
+        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
+
+        mImageFetcher.fulfillLargeIconRequests();
+
+        // The load should now be complete.
+        verify(mTileGroupDelegate).onLoadingComplete(any());
+        verify(mTileGroupObserver).onTileIconChanged(eq(tile));
+        verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class));
+    }
+
+    @Test
+    public void testIconLoadingWhenTileNotRegistered() {
+        TileGroup tileGroup = initialiseTileGroup();
+        Tile tile = new Tile(createSiteSuggestion("title", URLS[0]), 0);
+
+        ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null);
+        mTileRenderer.buildTileView(tile, layout, tileGroup.getTileSetupDelegate());
+
+        // Ensure we run the callback for the new tile.
+        assertEquals(1, mImageFetcher.getPendingIconCallbackCount());
+        mImageFetcher.fulfillLargeIconRequests();
+
+        verify(mTileGroupObserver, never()).onTileIconChanged(tile);
+    }
+
+    private TileGridViewHolder setupView(TileGroup tileGroup) {
+        TileGridLayout layout = new TileGridLayout(RuntimeEnvironment.application, null);
+        TileGridViewHolder tileGrid = new TileGridViewHolder(layout, 4, 2);
+        tileGrid.bindDataSource(tileGroup, mTileRenderer);
+        return tileGrid;
+    }
+
+    @Test
+    public void testIconLoading_Sync() {
+        TileGroup tileGroup = initialiseTileGroup();
+        mImageFetcher.fulfillLargeIconRequests();
+        reset(mTileGroupObserver, mTileGroupDelegate);
+
+        // Notify for a second set.
+        mMostVisitedSites.setTileSuggestions(URLS);
+        setupView(tileGroup).refreshData();
+        mImageFetcher.fulfillLargeIconRequests();
+
+        // Data changed but no loading complete event is sent
+        verify(mTileGroupObserver).onTileDataChanged();
+        verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class));
+        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
+    }
+
+    @Test
+    public void testIconLoading_AsyncNoTrack() {
+        TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true);
+        mImageFetcher.fulfillLargeIconRequests();
+        reset(mTileGroupObserver, mTileGroupDelegate);
+
+        // Notify for a second set.
+        mMostVisitedSites.setTileSuggestions(URLS);
+        tileGroup.onSwitchToForeground(/* trackLoadTask: */ false);
+        setupView(tileGroup).refreshData();
+        mImageFetcher.fulfillLargeIconRequests();
+
+        // Data changed but no loading complete event is sent (same as sync)
+        verify(mTileGroupObserver).onTileDataChanged();
+        verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class));
+        verify(mTileGroupDelegate, never()).onLoadingComplete(any());
+    }
+
+    @Test
+    public void testIconLoading_AsyncTrack() {
+        TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true);
+        mImageFetcher.fulfillLargeIconRequests();
+        reset(mTileGroupObserver, mTileGroupDelegate);
+
+        // Notify for a second set.
+        mMostVisitedSites.setTileSuggestions(URLS);
+        tileGroup.onSwitchToForeground(/* trackLoadTask: */ true);
+        setupView(tileGroup).refreshData();
+        mImageFetcher.fulfillLargeIconRequests();
+
+        // Data changed but no loading complete event is sent
+        verify(mTileGroupObserver).onTileDataChanged();
+        verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class));
+        verify(mTileGroupDelegate).onLoadingComplete(any());
+    }
+
+    /** {@link #initialiseTileGroup(boolean, String...)} override that does not defer loads. */
+    private TileGroup initialiseTileGroup(String... urls) {
+        return initialiseTileGroup(false, urls);
+    }
+
+    /**
+     * @param deferLoad whether to defer the load until
+     *                  {@link TileGroup#onSwitchToForeground(boolean)} is called. Works by
+     *                  pretending that the UI is visible.
+     * @param urls URLs used to initialise the tile group.
+     */
+    private TileGroup initialiseTileGroup(boolean deferLoad, String... urls) {
+        SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+        when(uiDelegate.getImageFetcher()).thenReturn(mImageFetcher);
+        when(uiDelegate.isVisible()).thenReturn(deferLoad);
+
+        mMostVisitedSites.setTileSuggestions(urls);
+
+        TileGroup tileGroup =
+                new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class),
+                        mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class));
+        tileGroup.startObserving(MAX_TILES_TO_FETCH);
+        setupView(tileGroup).refreshData();
+
+        reset(mTileGroupObserver);
+        reset(mTileGroupDelegate);
+        return tileGroup;
+    }
+
+    private class FakeImageFetcher extends ImageFetcher {
+        private final List<LargeIconCallback> mCallbackList = new ArrayList<>();
+
+        public FakeImageFetcher() {
+            super(null, null, null);
+        }
+
+        @Override
+        public void makeLargeIconRequest(String url, int size, LargeIconCallback callback) {
+            mCallbackList.add(callback);
+        }
+
+        public void fulfillLargeIconRequests(Bitmap bitmap, int color, boolean isColorDefault) {
+            for (LargeIconCallback callback : mCallbackList) {
+                callback.onLargeIconAvailable(bitmap, color, isColorDefault, IconType.INVALID);
+            }
+            mCallbackList.clear();
+        }
+
+        public int getPendingIconCallbackCount() {
+            return mCallbackList.size();
+        }
+
+        public void fulfillLargeIconRequests() {
+            fulfillLargeIconRequests(mock(Bitmap.class), Color.BLACK, false);
+        }
+    }
+}
diff --git a/chrome/android/touchless/java/DEPS b/chrome/android/touchless/java/DEPS
index 838459a9..81c3f45e 100644
--- a/chrome/android/touchless/java/DEPS
+++ b/chrome/android/touchless/java/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+components/feature_engagement/public",
   "+content/public/android/java/src/org/chromium/content_public",
 ]
diff --git a/chrome/android/touchless/java/res/layout/touchless_explore_sites_category_card_view.xml b/chrome/android/touchless/java/res/layout/touchless_explore_sites_category_card_view.xml
index 45803dd..a3312e0 100644
--- a/chrome/android/touchless/java/res/layout/touchless_explore_sites_category_card_view.xml
+++ b/chrome/android/touchless/java/res/layout/touchless_explore_sites_category_card_view.xml
@@ -24,7 +24,7 @@
         android:textAppearance="@style/TextAppearance.BlackBodyDefault"
         tools:text="Category" />
 
-    <org.chromium.chrome.browser.suggestions.TileGridLayout
+    <org.chromium.chrome.browser.suggestions.tile.TileGridLayout
         android:id="@+id/category_sites"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java
index c079824..c1c96787 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java
@@ -41,6 +41,8 @@
 class OpenLastTabMediator extends EmptyTabObserver
         implements HistoryProvider.BrowsingHistoryObserver, FocusableComponent {
     private static final String FIRST_LAUNCHED_KEY = "TOUCHLESS_WAS_FIRST_LAUNCHED";
+    private static final String LAST_REMOVED_VISIT_TIMESTAMP_KEY =
+            "TOUCHLESS_LAST_REMOVED_VISIT_TIMESTAMP";
 
     private final Context mContext;
     private final Profile mProfile;
@@ -52,6 +54,10 @@
     private final RoundedIconGenerator mIconGenerator;
     private final LargeIconBridge mIconBridge;
 
+    private HistoryItem mHistoryItem;
+    private boolean mPreferencesRead;
+    private long mLastRemovedVisitTimestamp = Long.MIN_VALUE;
+
     OpenLastTabMediator(Context context, Profile profile, NativePageHost nativePageHost,
             PropertyModel model, OpenLastTabView view) {
         mModel = model;
@@ -67,22 +73,33 @@
                 ViewUtils.createDefaultRoundedIconGenerator(mContext.getResources(), false);
         mIconBridge = new LargeIconBridge(mProfile);
 
-        PostTask.postTask(TaskTraits.USER_VISIBLE, () -> {
-            // Check if this is a first launch of Chrome.
-            SharedPreferences prefs = mNativePageHost.getActiveTab().getActivity().getPreferences(
-                    Context.MODE_PRIVATE);
-            boolean firstLaunched = prefs.getBoolean(FIRST_LAUNCHED_KEY, true);
-            prefs.edit().putBoolean(FIRST_LAUNCHED_KEY, false).apply();
-            PostTask.postTask(UiThreadTaskTraits.USER_VISIBLE, () -> {
-                mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_FIRST_LAUNCH, firstLaunched);
-            });
-        });
+        readPreferences();
 
         // TODO(wylieb):Investigate adding an item limit to the API.
         // Query the history for everything (no API exists to only query for the most recent).
         refreshHistoryData();
     }
 
+    private SharedPreferences getSharedPreferences() {
+        return mNativePageHost.getActiveTab().getActivity().getPreferences(Context.MODE_PRIVATE);
+    }
+
+    private void readPreferences() {
+        PostTask.postTask(TaskTraits.USER_VISIBLE, () -> {
+            // Check if this is a first launch of Chrome.
+            SharedPreferences prefs = getSharedPreferences();
+            boolean firstLaunched = prefs.getBoolean(FIRST_LAUNCHED_KEY, true);
+            prefs.edit().putBoolean(FIRST_LAUNCHED_KEY, false).apply();
+            mLastRemovedVisitTimestamp =
+                    prefs.getLong(LAST_REMOVED_VISIT_TIMESTAMP_KEY, Long.MIN_VALUE);
+            PostTask.postTask(UiThreadTaskTraits.USER_VISIBLE, () -> {
+                mPreferencesRead = true;
+                mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_FIRST_LAUNCH, firstLaunched);
+                updateModel();
+            });
+        });
+    }
+
     void destroy() {
         if (mHistoryBridge != null) {
             mHistoryBridge.destroy();
@@ -115,41 +132,55 @@
     @Override
     public void onQueryHistoryComplete(List<HistoryItem> items, boolean hasMorePotentialMatches) {
         if (items.size() == 0) {
+            mHistoryItem = null;
+        } else {
+            // First item is most recent.
+            mHistoryItem = items.get(0);
+        }
+        if (mPreferencesRead) {
+            updateModel();
+        }
+    }
+
+    // updateModel is only called after preferences are read. It populates model with data from
+    // mHistoryItem.
+    private void updateModel() {
+        if (mHistoryItem == null || mHistoryItem.getTimestamp() <= mLastRemovedVisitTimestamp) {
             // Consider the case where the history has nothing in it to be a failure.
             mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_LOAD_SUCCESS, false);
             return;
         }
 
-        // First item is most recent.
-        HistoryItem item = items.get(0);
-
-        String title = UrlUtilities.getDomainAndRegistry(item.getUrl(), false);
+        String title = UrlUtilities.getDomainAndRegistry(mHistoryItem.getUrl(), false);
         // Default the timestamp to happening just now. If it happened over a minute ago, calculate
         // and set the relative timestamp string.
         String timestamp = mContext.getResources().getString(R.string.open_last_tab_just_now);
         long now = System.currentTimeMillis();
-        if (now - item.getTimestamp() > MINUTE_IN_MILLIS) {
-            timestamp = getRelativeTimeSpanString(item.getTimestamp(), now, MINUTE_IN_MILLIS)
-                                .toString();
+        if (now - mHistoryItem.getTimestamp() > MINUTE_IN_MILLIS) {
+            timestamp =
+                    getRelativeTimeSpanString(mHistoryItem.getTimestamp(), now, MINUTE_IN_MILLIS)
+                            .toString();
         }
 
         mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_TITLE, title);
         mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_TIMESTAMP, timestamp);
-        boolean willReturnIcon = mIconBridge.getLargeIconForUrl(item.getUrl(),
+        boolean willReturnIcon = mIconBridge.getLargeIconForUrl(mHistoryItem.getUrl(),
                 mContext.getResources().getDimensionPixelSize(R.dimen.open_last_tab_icon_size),
                 (icon, fallbackColor, isFallbackColorDefault, iconType) -> {
-                    setAndShowButton(item.getUrl(), icon, fallbackColor, title);
+                    setAndShowButton(mHistoryItem.getUrl(), icon, fallbackColor, title);
                 });
 
         // False if icon bridge won't call us back.
         if (!willReturnIcon) {
-            setAndShowButton(item.getUrl(), null, R.color.default_icon_color, title);
+            setAndShowButton(mHistoryItem.getUrl(), null, R.color.default_icon_color, title);
         }
         mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_LOAD_SUCCESS, true);
     }
 
     @Override
-    public void onHistoryDeleted() {}
+    public void onHistoryDeleted() {
+        refreshHistoryData();
+    }
 
     @Override
     public void hasOtherFormsOfBrowsingData(boolean hasOtherForms) {}
@@ -172,7 +203,22 @@
                     @Override
                     public boolean isItemSupported(
                             @ContextMenuManager.ContextMenuItemId int menuItemId) {
-                        return menuItemId == ContextMenuManager.ContextMenuItemId.ADD_TO_MY_APPS;
+                        return menuItemId == ContextMenuManager.ContextMenuItemId.ADD_TO_MY_APPS
+                                || menuItemId == ContextMenuManager.ContextMenuItemId.REMOVE;
+                    }
+
+                    @Override
+                    public void removeItem() {
+                        mLastRemovedVisitTimestamp = mHistoryItem.getTimestamp();
+                        PostTask.postTask(TaskTraits.USER_VISIBLE, () -> {
+                            SharedPreferences prefs = getSharedPreferences();
+                            prefs.edit()
+                                    .putLong(LAST_REMOVED_VISIT_TIMESTAMP_KEY,
+                                            mLastRemovedVisitTimestamp)
+                                    .apply();
+                        });
+                        mHistoryBridge.markItemForRemoval(mHistoryItem);
+                        mHistoryBridge.removeItems();
                     }
 
                     @Override
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java
index 0c22dd26..e19bb5a 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java
@@ -16,9 +16,9 @@
 import org.chromium.chrome.browser.explore_sites.ExploreSitesEnums;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.suggestions.ImageFetcher;
-import org.chromium.chrome.browser.suggestions.MostVisitedSites;
 import org.chromium.chrome.browser.suggestions.SiteSuggestion;
 import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites;
 import org.chromium.chrome.touchless.R;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java
index 06a1f093..4106eb0 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java
@@ -7,6 +7,9 @@
 import android.view.LayoutInflater;
 
 import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.touchless.ui.tooltip.TooltipView;
 import org.chromium.chrome.touchless.R;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -27,7 +30,9 @@
                 (KeyFunctionsIPHView) LayoutInflater.from(tooltipView.getContext())
                         .inflate(R.layout.notouch_key_functions_view, null);
         PropertyModelChangeProcessor.create(model, view, KeyFunctionsIPHViewBinder::bind);
-        mMediator = new KeyFunctionsIPHMediator(model, activityTabProvider);
+        mMediator = new KeyFunctionsIPHMediator(model, activityTabProvider,
+                ChromePreferenceManager.getInstance(),
+                TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile()));
     }
 
     public void destroy() {
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
index 90461bd..1194c58 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
@@ -9,11 +9,10 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.dom_distiller.TabDistillabilityProvider;
-import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.feature_engagement.Tracker.DisplayLockHandle;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -30,9 +29,11 @@
 public class KeyFunctionsIPHMediator implements CursorObserver {
     private final PropertyModel mModel;
     private final KeyFunctionsIPHTabObserver mKeyFunctionsIPHTabObserver;
+    private final ChromePreferenceManager mChromePreferenceManager;
+    private final Tracker mTracker;
     private FutureTask mHideTask;
-    private int mPageLoadCount;
     private DisplayLockHandle mDisplayLockHandle;
+    private int mPageLoadCount;
     private boolean mIsFallbackCursorModeOn;
     private boolean mShowedWhenPageLoadStarted;
 
@@ -40,8 +41,8 @@
 
     // For the first INTRODUCTORY_SESSIONS sessions, show the IPH every INTRODUCTORY_PAGE_LOAD_CYCLE
     // page loads.
-    private static final int INTRODUCTORY_SESSIONS = 6;
-    private static final int INTRODUCTORY_PAGE_LOAD_CYCLE = 3;
+    static final int INTRODUCTORY_SESSIONS = 6;
+    static final int INTRODUCTORY_PAGE_LOAD_CYCLE = 3;
 
     @IntDef({DisplayCause.PAGE_LOAD_STARTED, DisplayCause.PAGE_LOAD_FINISHED,
             DisplayCause.FALLBACK_CURSOR_TOGGLED})
@@ -52,9 +53,12 @@
         int FALLBACK_CURSOR_TOGGLED = 2;
     }
 
-    KeyFunctionsIPHMediator(PropertyModel model, ActivityTabProvider activityTabProvider) {
+    KeyFunctionsIPHMediator(PropertyModel model, ActivityTabProvider activityTabProvider,
+            ChromePreferenceManager chromePreferenceManager, Tracker tracker) {
         mModel = model;
         mKeyFunctionsIPHTabObserver = new KeyFunctionsIPHTabObserver(activityTabProvider);
+        mChromePreferenceManager = chromePreferenceManager;
+        mTracker = tracker;
         TouchlessEventHandler.addCursorObserver(this);
     }
 
@@ -70,14 +74,13 @@
     private void show(@DisplayCause int displayCause) {
         if (displayCause == DisplayCause.PAGE_LOAD_STARTED) {
             mShowedWhenPageLoadStarted = false;
-            int totalSessionCount = ChromePreferenceManager.getInstance().readInt(
+            int totalSessionCount = mChromePreferenceManager.readInt(
                     ChromePreferenceManager.TOUCHLESS_BROWSING_SESSION_COUNT);
             if (totalSessionCount <= INTRODUCTORY_SESSIONS
                     && mPageLoadCount % INTRODUCTORY_PAGE_LOAD_CYCLE != 1) {
                 return;
             }
             if (totalSessionCount > INTRODUCTORY_SESSIONS && mPageLoadCount > 1) return;
-            mShowedWhenPageLoadStarted = true;
         } else if (mShowedWhenPageLoadStarted && displayCause == DisplayCause.PAGE_LOAD_FINISHED) {
             // If we have already shown the IPH when page load started, we should avoid showing it
             // again when page load is finished.
@@ -86,11 +89,12 @@
 
         // If we are already showing this IPH, we should release the lock.
         if (mDisplayLockHandle != null) mDisplayLockHandle.release();
-        mDisplayLockHandle = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile())
-                                     .acquireDisplayLock();
+        mDisplayLockHandle = mTracker.acquireDisplayLock();
         // If another IPH UI is currently shown, return.
         if (mDisplayLockHandle == null) return;
 
+        if (displayCause == DisplayCause.PAGE_LOAD_STARTED) mShowedWhenPageLoadStarted = true;
+
         if (mHideTask != null) mHideTask.cancel(false);
         mHideTask = new FutureTask<Void>(() -> {
             mModel.set(KeyFunctionsIPHProperties.IS_VISIBLE, false);
diff --git a/chrome/android/touchless/junit/DEPS b/chrome/android/touchless/junit/DEPS
new file mode 100644
index 0000000..2e2fc77
--- /dev/null
+++ b/chrome/android/touchless/junit/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/feature_engagement/public",
+]
diff --git a/chrome/android/touchless/junit/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediatorTest.java b/chrome/android/touchless/junit/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediatorTest.java
new file mode 100644
index 0000000..ed68378
--- /dev/null
+++ b/chrome/android/touchless/junit/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediatorTest.java
@@ -0,0 +1,126 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.touchless.ui.iph;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.task.test.ShadowPostTask;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabObserver;
+import org.chromium.components.feature_engagement.Tracker;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Unit tests for the {@link KeyFunctionsIPHMediator} class.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowPostTask.class})
+public class KeyFunctionsIPHMediatorTest {
+    private static final String SAMPLE_URL = "https://google.com/chrome/";
+
+    private PropertyModel mModel;
+    private ArgumentCaptor<TabObserver> mTabObserver;
+    private Tab mTab;
+    private ChromePreferenceManager mChromePreferenceManager;
+    private Tracker mTracker;
+    private KeyFunctionsIPHMediator mKeyFunctionsIPHMediator;
+
+    @Before
+    public void setUp() {
+        mTab = mock(Tab.class);
+        mChromePreferenceManager = mock(ChromePreferenceManager.class);
+        mTracker = mock(Tracker.class);
+        mTabObserver = ArgumentCaptor.forClass(TabObserver.class);
+        mModel = spy(new PropertyModel.Builder(KeyFunctionsIPHProperties.ALL_KEYS).build());
+        ActivityTabProvider activityTabProvider = spy(ActivityTabProvider.class);
+
+        TrackerFactory.setTrackerForTests(mTracker);
+        when(mTracker.acquireDisplayLock()).thenReturn(mock(Tracker.DisplayLockHandle.class));
+        when(activityTabProvider.get()).thenReturn(mTab);
+        when(mChromePreferenceManager.readInt(
+                     ChromePreferenceManager.TOUCHLESS_BROWSING_SESSION_COUNT))
+                .thenReturn(0);
+        mKeyFunctionsIPHMediator = spy(new KeyFunctionsIPHMediator(
+                mModel, activityTabProvider, mChromePreferenceManager, mTracker));
+        verify(mTab).addObserver(mTabObserver.capture());
+    }
+
+    @Test
+    public void visibilityTest() {
+        when(mTab.getUrl()).thenReturn(SAMPLE_URL);
+        mTabObserver.getValue().onPageLoadStarted(mTab, SAMPLE_URL);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE), false);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false);
+
+        for (int i = 1; i < KeyFunctionsIPHMediator.INTRODUCTORY_PAGE_LOAD_CYCLE; i++) {
+            mTabObserver.getValue().onPageLoadStarted(mTab, SAMPLE_URL);
+            Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false);
+        }
+
+        mTabObserver.getValue().onPageLoadStarted(mTab, SAMPLE_URL);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+        when(mChromePreferenceManager.readInt(
+                     ChromePreferenceManager.TOUCHLESS_BROWSING_SESSION_COUNT))
+                .thenReturn(KeyFunctionsIPHMediator.INTRODUCTORY_SESSIONS + 1);
+        for (int i = 0; i <= KeyFunctionsIPHMediator.INTRODUCTORY_PAGE_LOAD_CYCLE; i++) {
+            mTabObserver.getValue().onPageLoadStarted(mTab, SAMPLE_URL);
+            Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false);
+        }
+    }
+
+    @Test
+    public void cursorVisibilityToggleTest() {
+        mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(true);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE), true);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true);
+
+        mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(false);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE), false);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true);
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false);
+
+        mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(true);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE), true);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true);
+    }
+
+    @Test
+    public void otherIPHShowingTest() {
+        when(mTracker.acquireDisplayLock()).thenReturn(null);
+        mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(true);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false);
+
+        when(mTracker.acquireDisplayLock()).thenReturn(mock(Tracker.DisplayLockHandle.class));
+        mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(false);
+        Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true);
+    }
+
+    @After
+    public void tearDown() {
+        TrackerFactory.setTrackerForTests(null);
+    }
+}
diff --git a/chrome/android/touchless/touchless_java_sources.gni b/chrome/android/touchless/touchless_java_sources.gni
index fbba84c6..d062140 100644
--- a/chrome/android/touchless/touchless_java_sources.gni
+++ b/chrome/android/touchless/touchless_java_sources.gni
@@ -75,6 +75,7 @@
 touchless_junit_test_java_sources = [
   "touchless/junit/src/org/chromium/chrome/browser/touchless/ScrollPositionInfoTest.java",
   "touchless/junit/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolderTest.java",
+  "touchless/junit/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediatorTest.java",
   "touchless/junit/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarMediatorTest.java",
 ]
 
diff --git a/chrome/android/webapk/PRESUBMIT.py b/chrome/android/webapk/PRESUBMIT.py
index 70d259d..9a1474d4 100644
--- a/chrome/android/webapk/PRESUBMIT.py
+++ b/chrome/android/webapk/PRESUBMIT.py
@@ -7,12 +7,18 @@
 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
 for more details about the presubmit API built into depot_tools.
 
-This presubmit checks for two rules:
+This presubmit checks for three rules:
 1. If anything in the webapk/libs/common or the webapk/shell_apk directories
 has changed (excluding test files), $CURRENT_VERSION_VARIABLE should be updated.
 2. If $REQUEST_UPDATE_FOR_VERSION_VARIABLE in
 $REQUEST_UPDATE_FOR_VERSION_LOCAL_PATH is changed, the variable change should
 be the only change in the CL.
+3. If a file in a res/ directory has been added, its' file name should be
+unique to the res/ directory.
+  res/values/dimens.xml and res/values-v17/dimens.xml -> OK
+  res/values/dimens.xml and libs/common/res_splash/values/dimens.xml -> BAD
+This requirement is needed to upload the resources to the Google storage
+build bucket.
 """
 
 CURRENT_VERSION_VARIABLE = 'current_shell_apk_version'
@@ -24,11 +30,14 @@
 
 TRIGGER_CURRENT_VERSION_UPDATE_LOCAL_PATHS = [
     'libs/common/src/',
+    'libs/common/res_splash/',
     'shell_apk/AndroidManifest.xml',
     'shell_apk/res/',
     'shell_apk/src/',
 ]
 
+RES_DIR_LOCAL_PATHS = ['shell_apk/res', 'libs/common/res_splash']
+
 def _DoChangedContentsContain(changed_contents, key):
   for _, line in changed_contents:
     if key in line:
@@ -36,6 +45,19 @@
   return False
 
 
+def _FindFileNamesInDirectory(input_api, dir_path, search_file_names):
+  """
+  Searches the directory recursively for files with the passed-in file name
+  (not file path) set. Returns the file names of any matches.
+  """
+  matches = []
+  for _, _, file_names in input_api.os_walk(dir_path):
+    for file_name in file_names:
+      if file_name in search_file_names:
+        matches.append(file_name)
+  return matches
+
+
 def _CheckVersionVariableChanged(input_api, version_file_local_path,
                                  variable_name):
   for f in input_api.AffectedFiles():
@@ -92,11 +114,54 @@
   return []
 
 
+def _CheckNoOverlappingFileNamesInResourceDirsRule(input_api, output_api):
+  """
+  Checks that if a file has been added to a res/ directory that its file name
+  is unique to the res/ directory.
+    res/values/dimens.xml and res/values-v17/dimens.xml -> OK
+    res/values/dimens.xml and libs/common/res_splash/values/dimens.xml -> BAD
+  """
+  res_dir_file_names_map = {}
+  for f in input_api.AffectedFiles():
+    local_path = input_api.os_path.relpath(f.AbsoluteLocalPath(),
+                                           input_api.PresubmitLocalPath())
+    for res_dir_local_path in RES_DIR_LOCAL_PATHS:
+      if local_path.startswith(res_dir_local_path):
+        file_name = input_api.os_path.basename(local_path)
+        res_dir_file_names_map.setdefault(res_dir_local_path, set()).add(
+            file_name)
+        break
+
+  if len(res_dir_file_names_map) == 0:
+    return []
+
+  overlapping_file_names = set()
+  for res_dir, file_names in res_dir_file_names_map.items():
+    for other_res_dir, other_file_names in res_dir_file_names_map.items():
+      if res_dir == other_res_dir:
+        continue
+
+      # Check for affected files with identical name in |other_res_dir|.
+      overlapping_file_names |= (file_names & other_file_names)
+
+      # Check for existing files with identical name in |other_res_dir|.
+      overlapping_file_names.update(
+          _FindFileNamesInDirectory(input_api, other_res_dir, file_names))
+
+  if len(overlapping_file_names) > 0:
+    error_msg = ('Resources in different top level res/ directories {} should '
+                 'have different names:').format(RES_DIR_LOCAL_PATHS)
+    return [output_api.PresubmitError(error_msg,
+                                      items=list(overlapping_file_names))]
+  return []
+
 def _CommonChecks(input_api, output_api):
   """Checks common to both upload and commit."""
   result = []
   result.extend(_CheckChromeUpdateTriggerRule(input_api, output_api))
   result.extend(_CheckCurrentVersionIncreaseRule(input_api, output_api))
+  result.extend(_CheckNoOverlappingFileNamesInResourceDirsRule(input_api,
+                                                               output_api))
 
   return result
 
diff --git a/chrome/android/webapk/PRESUBMIT_test.py b/chrome/android/webapk/PRESUBMIT_test.py
index db2368d..674f780 100755
--- a/chrome/android/webapk/PRESUBMIT_test.py
+++ b/chrome/android/webapk/PRESUBMIT_test.py
@@ -14,12 +14,36 @@
 from PRESUBMIT_test_mocks import MockAffectedFile
 from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
 
-class ShellApkVersion(unittest.TestCase):
-   UPDATE_CURRENT_VERSION_MESSAGE = (
-       'current_shell_apk_version in '
-       'shell_apk/current_version/current_version.gni needs to updated due to '
-       'changes in:')
+# Mocks os.walk()
+class MockOsWalkFileSystem(object):
+  def __init__(self, file_paths):
+    self.file_paths = file_paths
 
+  def walk(self, top):
+    if not top.endswith('/'):
+      top += '/'
+
+    files = []
+    dirs = []
+    for f in self.file_paths:
+      if f.startswith(top):
+        remaining = f[len(top):]
+        slash_index = remaining.find('/')
+        if slash_index >= 0:
+          dir_name = remaining[:slash_index]
+          if not dir_name in dirs:
+            dirs.append(dir_name)
+        else:
+          files.append(remaining)
+
+      yield top[:-1], dirs, files
+
+    for name in dirs:
+      for result in self.walk(top + name):
+        yield result
+
+
+class CustomMockInputApi(MockInputApi):
    def makeMockAffectedFiles(self, file_names):
      mock_files = []
      for file_name in file_names:
@@ -27,6 +51,13 @@
            MockAffectedFile(file_name, ['new_content'], action='A'))
      return mock_files
 
+
+class ShellApkVersion(unittest.TestCase):
+   UPDATE_CURRENT_VERSION_MESSAGE = (
+       'current_shell_apk_version in '
+       'shell_apk/current_version/current_version.gni needs to updated due to '
+       'changes in:')
+
    def testCheckWamMintTriggerRule(self):
      COMMON_SRC_FILE_PATH = (
          'libs/common/src/org/chromium/webapk/lib/common/A.java')
@@ -46,8 +77,8 @@
 
      # template_shell_apk_version not updated. There should be a warning about
      # template_shell_apk_version needing to be updated.
-     input_api = MockInputApi()
-     input_api.files = self.makeMockAffectedFiles(
+     input_api = CustomMockInputApi()
+     input_api.files = input_api.makeMockAffectedFiles(
          changed_java_file_paths + [SHELL_APK_RES_FILE_PATH])
      input_api.files += [
          MockAffectedFile(CURRENT_VERSION_FILE_PATH, 'variable=O',
@@ -62,7 +93,7 @@
                       warnings[0].items)
 
      # template_shell_apk_version updated. There should be no warnings.
-     input_api.files = self.makeMockAffectedFiles(
+     input_api.files = input_api.makeMockAffectedFiles(
          changed_java_file_paths + [SHELL_APK_RES_FILE_PATH])
      input_api.files += [
          MockAffectedFile(CURRENT_VERSION_FILE_PATH,
@@ -73,5 +104,59 @@
                                                            MockOutputApi())
      self.assertEqual([], warnings)
 
+
+class OverlappingResourceFileNames(unittest.TestCase):
+   RESOURCES_SHOULD_HAVE_DIFFERENT_FILE_NAMES_MESSAGE = (
+       'Resources in different top level res/ directories [\'shell_apk/res\', '
+       '\'libs/common/res_splash\'] should have different names:')
+
+   def testAddFileSameNameWithinResDirectory(self):
+     # Files within a res/ directory can have same file name.
+     MOCK_FILE_SYSTEM_FILES = ['shell_apk/res/values/colors.xml',
+                               'libs/common/res_splash/values/dimens.xml']
+     input_api = CustomMockInputApi()
+     input_api.os_walk = MockOsWalkFileSystem(MOCK_FILE_SYSTEM_FILES).walk
+
+     input_api.files = input_api.makeMockAffectedFiles([
+         'shell_apk/res/values-v22/values.xml'])
+     errors = PRESUBMIT._CheckNoOverlappingFileNamesInResourceDirsRule(
+         input_api, MockOutputApi())
+     self.assertEqual(0, len(errors))
+
+   def testAddFileSameNameAcrossResDirectories(self):
+     # Adding a file to a res/ directory with the same file name as a file in a
+     # different res/ directory is illegal.
+     MOCK_FILE_SYSTEM_FILES = ['shell_apk/res/values/colors.xml',
+                               'libs/common/res_splash/values/dimens.xml']
+     input_api = CustomMockInputApi()
+     input_api.os_walk = MockOsWalkFileSystem(MOCK_FILE_SYSTEM_FILES).walk
+     input_api.files = input_api.makeMockAffectedFiles([
+         'shell_apk/res/values-v17/dimens.xml',
+         'libs/common/res_splash/values-v22/colors.xml'])
+     errors = PRESUBMIT._CheckNoOverlappingFileNamesInResourceDirsRule(
+         input_api, MockOutputApi())
+     self.assertEqual(1, len(errors))
+     self.assertEqual(self.RESOURCES_SHOULD_HAVE_DIFFERENT_FILE_NAMES_MESSAGE,
+                      errors[0].message)
+     errors[0].items.sort()
+     self.assertEqual(['colors.xml', 'dimens.xml'], errors[0].items)
+
+   def testAddTwoFilesWithSameNameDifferentResDirectories(self):
+     # Adding two files with the same file name but in different res/
+     # directories is illegal.
+     MOCK_FILE_SYSTEM_FILES = ['shell_apk/res/values/colors.xml',
+                               'libs/common/res_splash/values/dimens.xml']
+     input_api = CustomMockInputApi()
+     input_api.os_walk = MockOsWalkFileSystem(MOCK_FILE_SYSTEM_FILES).walk
+     input_api.files = input_api.makeMockAffectedFiles([
+         'shell_apk/res/values/values.xml',
+         'libs/common/res_splash/values-v22/values.xml'])
+     errors = PRESUBMIT._CheckNoOverlappingFileNamesInResourceDirsRule(
+         input_api, MockOutputApi())
+     self.assertEqual(1, len(errors))
+     self.assertEqual(self.RESOURCES_SHOULD_HAVE_DIFFERENT_FILE_NAMES_MESSAGE,
+                      errors[0].message)
+     self.assertEqual(['values.xml'], errors[0].items)
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/chrome/android/webapk/libs/common/res_splash/values-sw600dp/dimens.xml b/chrome/android/webapk/libs/common/res_splash/values-sw600dp/common_dimens.xml
similarity index 100%
rename from chrome/android/webapk/libs/common/res_splash/values-sw600dp/dimens.xml
rename to chrome/android/webapk/libs/common/res_splash/values-sw600dp/common_dimens.xml
diff --git a/chrome/android/webapk/libs/common/res_splash/values-v17/styles.xml b/chrome/android/webapk/libs/common/res_splash/values-v17/common_styles.xml
similarity index 100%
rename from chrome/android/webapk/libs/common/res_splash/values-v17/styles.xml
rename to chrome/android/webapk/libs/common/res_splash/values-v17/common_styles.xml
diff --git a/chrome/android/webapk/libs/common/res_splash/values/colors.xml b/chrome/android/webapk/libs/common/res_splash/values/common_colors.xml
similarity index 100%
rename from chrome/android/webapk/libs/common/res_splash/values/colors.xml
rename to chrome/android/webapk/libs/common/res_splash/values/common_colors.xml
diff --git a/chrome/android/webapk/libs/common/res_splash/values/dimens.xml b/chrome/android/webapk/libs/common/res_splash/values/common_dimens.xml
similarity index 100%
rename from chrome/android/webapk/libs/common/res_splash/values/dimens.xml
rename to chrome/android/webapk/libs/common/res_splash/values/common_dimens.xml
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 3123c861..51b231c 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 91
+current_shell_apk_version = 92
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 5b8547f..93c5623 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -135,6 +135,14 @@
   "+chrome/browser/ui/views/extensions/request_file_system_dialog_view.h",
   "+chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h",
 
+  # Ensure that only the public interface of performance_manager gets used.
+  "-chrome/browser/performance_manager",
+  "+chrome/browser/performance_manager/public",
+  "+chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.h",
+  "+chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.h",
+  "+chrome/browser/performance_manager/performance_manager.h",
+  "+chrome/browser/performance_manager/performance_manager_tab_helper.h",
+
   # Explicitly disallow using SyncMessageFilter to prevent browser from
   # sending synchronous IPC messages on non-UI threads.
   "-ipc/ipc_sync_message_filter.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 35f52891..01da6001 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2141,6 +2141,10 @@
      SINGLE_VALUE_TYPE(
          ::switches::
              kEnableExperimentalAccessibilityChromeVoxRichTextIndication)},
+    {"enable-experimental-kernel-vm-support",
+     flag_descriptions::kKernelnextVMsName,
+     flag_descriptions::kKernelnextVMsDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kKernelnextVMs)},
 #endif  // OS_CHROMEOS
 #if !defined(OS_ANDROID) && defined(GOOGLE_CHROME_BUILD)
     {"enable-google-branded-context-menu",
@@ -2691,11 +2695,6 @@
      flag_descriptions::kDirectManipulationStylusDescription,
      kOsWin | kOsMac | kOsLinux,
      FEATURE_VALUE_TYPE(features::kDirectManipulationStylus)},
-
-    {"show-managed-ui", flag_descriptions::kShowManagedUiName,
-     flag_descriptions::kShowManagedUiDescription,
-     kOsWin | kOsMac | kOsLinux | kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kShowManagedUi)},
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/apps/app_service/app_icon_source.cc b/chrome/browser/apps/app_service/app_icon_source.cc
index 92b97cd..a919a8e6 100644
--- a/chrome/browser/apps/app_service/app_icon_source.cc
+++ b/chrome/browser/apps/app_service/app_icon_source.cc
@@ -52,7 +52,7 @@
 
 AppIconSource::~AppIconSource() = default;
 
-std::string AppIconSource::GetSource() const {
+std::string AppIconSource::GetSource() {
   return chrome::kChromeUIAppIconHost;
 }
 
@@ -95,18 +95,18 @@
       allow_placeholder_icon, base::BindOnce(&RunCallback, callback));
 }
 
-std::string AppIconSource::GetMimeType(const std::string&) const {
+std::string AppIconSource::GetMimeType(const std::string&) {
   // We need to explicitly return a mime type, otherwise if the user tries to
   // drag the image they get no extension.
   return "image/png";
 }
 
-bool AppIconSource::AllowCaching() const {
+bool AppIconSource::AllowCaching() {
   // Should not be cached as caching is performed by proxy.
   return false;
 }
 
-bool AppIconSource::ShouldReplaceExistingSource() const {
+bool AppIconSource::ShouldReplaceExistingSource() {
   // The source doesn't maintain its own state so there's no need to replace it.
   return false;
 }
diff --git a/chrome/browser/apps/app_service/app_icon_source.h b/chrome/browser/apps/app_service/app_icon_source.h
index 9d2a9573..345a33a 100644
--- a/chrome/browser/apps/app_service/app_icon_source.h
+++ b/chrome/browser/apps/app_service/app_icon_source.h
@@ -35,14 +35,14 @@
   ~AppIconSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string&) const override;
-  bool AllowCaching() const override;
-  bool ShouldReplaceExistingSource() const override;
+  std::string GetMimeType(const std::string&) override;
+  bool AllowCaching() override;
+  bool ShouldReplaceExistingSource() override;
 
  private:
   Profile* profile_;
diff --git a/chrome/browser/chrome_service_worker_browsertest.cc b/chrome/browser/chrome_service_worker_browsertest.cc
index f79cb799..0b1527d 100644
--- a/chrome/browser/chrome_service_worker_browsertest.cc
+++ b/chrome/browser/chrome_service_worker_browsertest.cc
@@ -719,7 +719,7 @@
   ~StaticURLDataSource() override = default;
 
   // content::URLDataSource:
-  std::string GetSource() const override { return source_; }
+  std::string GetSource() override { return source_; }
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
@@ -727,10 +727,10 @@
     std::string data(content_);
     callback.Run(base::RefCountedString::TakeString(&data));
   }
-  std::string GetMimeType(const std::string& path) const override {
+  std::string GetMimeType(const std::string& path) override {
     return "application/javascript";
   }
-  bool ShouldAddContentSecurityPolicy() const override { return false; }
+  bool ShouldAddContentSecurityPolicy() override { return false; }
 
  private:
   const std::string source_;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 5842809..a817a83 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2331,7 +2331,6 @@
     "arc/tracing/arc_tracing_model_unittest.cc",
     "arc/tracing/arc_value_event_unittest.cc",
     "arc/tts/arc_tts_service_unittest.cc",
-    "arc/voice_interaction/fake_voice_interaction_controller.cc",
     "arc/voice_interaction/voice_interaction_controller_client_unittest.cc",
     "arc/wallpaper/arc_wallpaper_service_unittest.cc",
     "assistant/assistant_util_unittest.cc",
diff --git a/chrome/browser/chromeos/OWNERS b/chrome/browser/chromeos/OWNERS
index 3f4dcc4..41860dd6 100644
--- a/chrome/browser/chromeos/OWNERS
+++ b/chrome/browser/chromeos/OWNERS
@@ -4,7 +4,6 @@
 
 achuith@chromium.org
 alemate@chromium.org
-derat@chromium.org
 jamescook@chromium.org
 oshima@chromium.org
 satorux@chromium.org
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc
index 25f57a2..ca1bc33 100644
--- a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc
@@ -99,6 +99,9 @@
   ArcTracingModel::TracingEvents converted_events;
   std::map<uint32_t, std::vector<ArcTracingEvent*>>
       per_thread_pending_events_stack;
+
+  std::map<std::pair<char, std::string>, std::unique_ptr<ArcTracingEvent>>
+      pending_asynchronous_events;
 };
 
 bool HandleGraphicsEvent(GraphicsEventsContext* context,
@@ -158,6 +161,41 @@
       completed_event->SetPhase(TRACE_EVENT_PHASE_COMPLETE);
       completed_event->SetDuration(timestamp - completed_event->GetTimestamp());
     } break;
+    case TRACE_EVENT_PHASE_ASYNC_BEGIN:
+    case TRACE_EVENT_PHASE_ASYNC_END: {
+      const size_t name_pos = ParseUint32(line, event_position + 2, '|', &pid);
+      if (name_pos == std::string::npos) {
+        LOG(ERROR) << "Cannot parse pid of trace event: " << line;
+        return false;
+      }
+      const size_t id_pos = line.find('|', name_pos + 2);
+      if (id_pos == std::string::npos) {
+        LOG(ERROR) << "Cannot parse name|id of trace event: " << line;
+        return false;
+      }
+      const std::string name = line.substr(name_pos + 1, id_pos - name_pos - 1);
+      const std::string id = line.substr(id_pos + 1);
+      std::unique_ptr<ArcTracingEvent> event =
+          std::make_unique<ArcTracingEvent>(base::DictionaryValue());
+      event->SetPhase(phase);
+      event->SetPid(pid);
+      event->SetTid(tid);
+      event->SetTimestamp(timestamp);
+      event->SetCategory(kAndroidCategory);
+      event->SetName(name);
+      // Id here is weak and theoretically can be replicated in another
+      // processes or for different event names.
+      const std::string full_id = line.substr(event_position + 2);
+      event->SetId(id);
+      if (context->pending_asynchronous_events.find({phase, full_id}) !=
+          context->pending_asynchronous_events.end()) {
+        LOG(ERROR) << "Found duplicated asynchronous event " << line;
+        // That could be the real case from Android framework, for example
+        // animator:opacity trace. Ignore these duplicate events.
+        return true;
+      }
+      context->pending_asynchronous_events[{phase, full_id}] = std::move(event);
+    } break;
     default:
       LOG(ERROR) << "Unsupported type of trace event: " << line;
       return false;
@@ -280,6 +318,15 @@
   return true;
 }
 
+bool SortByTimestampPred(const std::unique_ptr<ArcTracingEvent>& lhs,
+                         const std::unique_ptr<ArcTracingEvent>& rhs) {
+  const uint64_t lhs_timestamp = lhs->GetTimestamp();
+  const uint64_t rhs_timestamp = rhs->GetTimestamp();
+  if (lhs_timestamp != rhs_timestamp)
+    return lhs_timestamp < rhs_timestamp;
+  return lhs->GetDuration() > rhs->GetDuration();
+}
+
 }  // namespace
 
 ArcTracingModel::ArcTracingModel() = default;
@@ -325,6 +372,11 @@
     return false;
   }
 
+  for (auto& group_events : group_events_) {
+    std::sort(group_events.second.begin(), group_events.second.end(),
+              SortByTimestampPred);
+  }
+
   return true;
 }
 
@@ -362,6 +414,17 @@
   return collector;
 }
 
+ArcTracingModel::TracingEventPtrs ArcTracingModel::GetGroupEvents(
+    const std::string& id) const {
+  TracingEventPtrs result;
+  const auto& it = group_events_.find(id);
+  if (it == group_events_.end())
+    return result;
+  for (const auto& group_event : it->second)
+    result.emplace_back(group_event.get());
+  return result;
+}
+
 bool ArcTracingModel::ProcessEvent(base::ListValue* events) {
   std::vector<std::unique_ptr<ArcTracingEvent>> parsed_events;
   for (auto& it : events->GetList()) {
@@ -400,14 +463,7 @@
 
   // Events may come by closure that means event started earlier as a root event
   // for others may appear after children. Sort by ts time.
-  std::sort(parsed_events.begin(), parsed_events.end(),
-            [](const auto& lhs, const auto& rhs) {
-              const uint64_t lhs_timestamp = lhs->GetTimestamp();
-              const uint64_t rhs_timestamp = rhs->GetTimestamp();
-              if (lhs_timestamp != rhs_timestamp)
-                return lhs_timestamp < rhs->GetTimestamp();
-              return lhs->GetDuration() > rhs->GetDuration();
-            });
+  std::sort(parsed_events.begin(), parsed_events.end(), SortByTimestampPred);
 
   for (auto& event : parsed_events) {
     switch (event->GetPhase()) {
@@ -541,6 +597,12 @@
     }
   }
 
+  for (auto& asyncronous_event :
+       graphics_events_context.pending_asynchronous_events) {
+    group_events_[asyncronous_event.second->GetId()].emplace_back(
+        std::move(asyncronous_event.second));
+  }
+
   // Close all pending tracing event, assuming last event is 0 duration.
   for (auto& pending_events :
        graphics_events_context.per_thread_pending_events_stack) {
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h
index 234f0b7..0e9347a7 100644
--- a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h
@@ -54,6 +54,9 @@
   TracingEventPtrs Select(const ArcTracingEvent* event,
                           const std::string query) const;
 
+  // Gets group of asynchronous events for |id|.
+  TracingEventPtrs GetGroupEvents(const std::string& id) const;
+
   // Dumps this model to |stream|.
   void Dump(std::ostream& stream) const;
 
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc
index 3fe0f3e3..4b6d57c7 100644
--- a/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/json/json_writer.h"
 #include "base/macros.h"
 #include "base/path_service.h"
+#include "base/trace_event/common/trace_event_common.h"
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_event.h"
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h"
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.h"
@@ -562,4 +563,43 @@
             events.buffer_events()[0][0].type);
 }
 
+TEST_F(ArcTracingModelTest, AsynchronousSystemEvents) {
+  base::FilePath base_path;
+  base::PathService::Get(chrome::DIR_TEST_DATA, &base_path);
+  const base::FilePath tracing_path = base_path.Append("arc_graphics_tracing")
+                                          .Append("trace_async_events.json");
+  std::string tracing_data;
+  base::ReadFileToString(tracing_path, &tracing_data);
+  DCHECK(!tracing_data.empty());
+
+  ArcTracingModel model;
+  ASSERT_TRUE(model.Build(tracing_data));
+
+  const ArcTracingModel::TracingEventPtrs group1 = model.GetGroupEvents("1");
+  const ArcTracingModel::TracingEventPtrs group2 = model.GetGroupEvents("2");
+
+  constexpr char kAsync1[] = "async1";
+  constexpr char kAsync2[] = "async2";
+
+  ASSERT_EQ(2U, group1.size());
+  EXPECT_EQ(kAsync1, group1[0]->GetName());
+  EXPECT_EQ(kAsync1, group1[1]->GetName());
+  EXPECT_EQ("1", group1[0]->GetId());
+  EXPECT_EQ(group1[0]->GetId(), group1[1]->GetId());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_BEGIN, group1[0]->GetPhase());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END, group1[1]->GetPhase());
+  EXPECT_EQ(1100000UL, group1[0]->GetTimestamp());
+  EXPECT_EQ(1300000UL, group1[1]->GetTimestamp());
+
+  ASSERT_EQ(2U, group2.size());
+  EXPECT_EQ(kAsync2, group2[0]->GetName());
+  EXPECT_EQ(kAsync2, group2[1]->GetName());
+  EXPECT_EQ("2", group2[0]->GetId());
+  EXPECT_EQ(group2[0]->GetId(), group2[1]->GetId());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_BEGIN, group2[0]->GetPhase());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END, group2[1]->GetPhase());
+  EXPECT_EQ(1200000UL, group2[0]->GetTimestamp());
+  EXPECT_EQ(1400000UL, group2[1]->GetTimestamp());
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
deleted file mode 100644
index 347406d..0000000
--- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h"
-
-namespace arc {
-
-FakeVoiceInteractionController::FakeVoiceInteractionController()
-    : binding_(this) {}
-
-FakeVoiceInteractionController::~FakeVoiceInteractionController() = default;
-
-ash::mojom::VoiceInteractionControllerPtr
-FakeVoiceInteractionController::CreateInterfacePtrAndBind() {
-  ash::mojom::VoiceInteractionControllerPtr ptr;
-  binding_.Bind(mojo::MakeRequest(&ptr));
-  return ptr;
-}
-
-void FakeVoiceInteractionController::NotifyStatusChanged(
-    ash::mojom::VoiceInteractionState state) {
-  voice_interaction_state_ = state;
-}
-
-void FakeVoiceInteractionController::NotifySettingsEnabled(bool enabled) {
-  voice_interaction_settings_enabled_ = enabled;
-}
-
-void FakeVoiceInteractionController::NotifyContextEnabled(bool enabled) {
-  voice_interaction_context_enabled_ = enabled;
-}
-
-void FakeVoiceInteractionController::NotifyHotwordAlwaysOn(bool enabled) {
-  voice_interaction_hotword_always_on_ = enabled;
-}
-
-void FakeVoiceInteractionController::NotifyHotwordEnabled(bool enabled) {
-  voice_interaction_hotword_enabled_ = enabled;
-}
-
-void FakeVoiceInteractionController::NotifyConsentStatus(
-    ash::mojom::ConsentStatus consent_status) {
-  consent_status_ = consent_status;
-}
-
-void FakeVoiceInteractionController::NotifyFeatureAllowed(
-    ash::mojom::AssistantAllowedState state) {
-  assistant_allowed_state_ = state;
-}
-
-void FakeVoiceInteractionController::NotifyNotificationEnabled(bool enabled) {
-  voice_interaction_notification_enabled_ = enabled;
-}
-
-void FakeVoiceInteractionController::NotifyLocaleChanged(
-    const std::string& locale) {
-  locale_ = locale;
-}
-
-void FakeVoiceInteractionController::NotifyLaunchWithMicOpen(
-    bool launch_with_mic_open) {
-  launch_with_mic_open_ = launch_with_mic_open;
-}
-
-void FakeVoiceInteractionController::NotifyArcPlayStoreEnabledChanged(
-    bool enabled) {
-  arc_play_store_enabled_ = enabled;
-}
-
-void FakeVoiceInteractionController::NotifyLockedFullScreenStateChanged(
-    bool enabled) {
-  locked_full_screen_enabled_ = enabled;
-}
-
-}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
deleted file mode 100644
index 1274d37..0000000
--- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_
-#define CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_
-
-#include <string>
-
-#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace arc {
-
-class FakeVoiceInteractionController
-    : public ash::mojom::VoiceInteractionController {
- public:
-  FakeVoiceInteractionController();
-  ~FakeVoiceInteractionController() override;
-
-  ash::mojom::VoiceInteractionControllerPtr CreateInterfacePtrAndBind();
-
-  // ash::mojom::VoiceInteractionController:
-  void NotifyStatusChanged(ash::mojom::VoiceInteractionState state) override;
-  void NotifySettingsEnabled(bool enabled) override;
-  void NotifyContextEnabled(bool enabled) override;
-  void NotifyHotwordAlwaysOn(bool enabled) override;
-  void NotifyHotwordEnabled(bool enabled) override;
-  void NotifyConsentStatus(ash::mojom::ConsentStatus consent_status) override;
-  void NotifyFeatureAllowed(ash::mojom::AssistantAllowedState state) override;
-  void NotifyNotificationEnabled(bool enabled) override;
-  void NotifyLocaleChanged(const std::string& locale) override;
-  void NotifyLaunchWithMicOpen(bool launch_with_mic_open) override;
-  void NotifyArcPlayStoreEnabledChanged(bool enabled) override;
-  void NotifyLockedFullScreenStateChanged(bool enabled) override;
-  void AddObserver(ash::mojom::VoiceInteractionObserverPtr observer) override {}
-
-  ash::mojom::VoiceInteractionState voice_interaction_state() const {
-    return voice_interaction_state_;
-  }
-  bool voice_interaction_settings_enabled() const {
-    return voice_interaction_settings_enabled_;
-  }
-  bool voice_interaction_context_enabled() const {
-    return voice_interaction_context_enabled_;
-  }
-  bool voice_interaction_hotword_enabled() const {
-    return voice_interaction_hotword_enabled_;
-  }
-  ash::mojom::ConsentStatus voice_interaction_consent_status() const {
-    return consent_status_;
-  }
-  ash::mojom::AssistantAllowedState assistant_allowed_state() const {
-    return assistant_allowed_state_;
-  }
-  bool voice_interaction_notification_enabled() const {
-    return voice_interaction_notification_enabled_;
-  }
-  const std::string& locale() const { return locale_; }
-  bool launch_with_mic_open() const { return launch_with_mic_open_; }
-  bool arc_play_store_enabled() const { return arc_play_store_enabled_; }
-  bool locked_full_screen_enabled() const {
-    return locked_full_screen_enabled_;
-  }
-
- private:
-  ash::mojom::VoiceInteractionState voice_interaction_state_ =
-      ash::mojom::VoiceInteractionState::STOPPED;
-  bool voice_interaction_settings_enabled_ = false;
-  bool voice_interaction_context_enabled_ = false;
-  bool voice_interaction_hotword_always_on_ = false;
-  bool voice_interaction_hotword_enabled_ = false;
-  ash::mojom::ConsentStatus consent_status_ =
-      ash::mojom::ConsentStatus::kUnknown;
-  bool voice_interaction_notification_enabled_ = false;
-  std::string locale_;
-  ash::mojom::AssistantAllowedState assistant_allowed_state_ =
-      ash::mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO;
-  bool launch_with_mic_open_ = false;
-  bool arc_play_store_enabled_ = false;
-  bool locked_full_screen_enabled_ = false;
-
-  mojo::Binding<ash::mojom::VoiceInteractionController> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeVoiceInteractionController);
-};
-
-}  // namespace arc
-
-#endif  // CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
index a3c901e..f1f0f2a 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "ash/public/cpp/ash_pref_names.h"
-#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "base/bind.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
@@ -38,7 +38,6 @@
 
 VoiceInteractionControllerClient::VoiceInteractionControllerClient() {
   DCHECK(!g_voice_interaction_controller_client_instance);
-  ConnectToVoiceInteractionController();
 
   notification_registrar_.Add(this,
                               chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
@@ -78,57 +77,49 @@
 void VoiceInteractionControllerClient::NotifyStatusChanged(
     ash::mojom::VoiceInteractionState state) {
   voice_interaction_state_ = state;
-  voice_interaction_controller_->NotifyStatusChanged(state);
+  ash::VoiceInteractionController::Get()->NotifyStatusChanged(state);
   for (auto& observer : observers_)
     observer.OnStateChanged(state);
 }
 
 void VoiceInteractionControllerClient::NotifyLockedFullScreenStateChanged(
     bool enabled) {
-  voice_interaction_controller_->NotifyLockedFullScreenStateChanged(enabled);
-}
-
-void VoiceInteractionControllerClient::SetControllerForTesting(
-    ash::mojom::VoiceInteractionControllerPtr controller) {
-  voice_interaction_controller_ = std::move(controller);
-}
-
-void VoiceInteractionControllerClient::FlushMojoForTesting() {
-  voice_interaction_controller_.FlushForTesting();
+  ash::VoiceInteractionController::Get()->NotifyLockedFullScreenStateChanged(
+      enabled);
 }
 
 void VoiceInteractionControllerClient::NotifySettingsEnabled() {
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
   bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionEnabled);
-  voice_interaction_controller_->NotifySettingsEnabled(enabled);
+  ash::VoiceInteractionController::Get()->NotifySettingsEnabled(enabled);
 }
 
 void VoiceInteractionControllerClient::NotifyContextEnabled() {
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
   bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionContextEnabled);
-  voice_interaction_controller_->NotifyContextEnabled(enabled);
+  ash::VoiceInteractionController::Get()->NotifyContextEnabled(enabled);
 }
 
 void VoiceInteractionControllerClient::NotifyHotwordEnabled() {
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
   bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled);
-  voice_interaction_controller_->NotifyHotwordEnabled(enabled);
+  ash::VoiceInteractionController::Get()->NotifyHotwordEnabled(enabled);
 }
 
 void VoiceInteractionControllerClient::NotifyHotwordAlwaysOn() {
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
   bool always_on = prefs->GetBoolean(prefs::kVoiceInteractionHotwordAlwaysOn);
-  voice_interaction_controller_->NotifyHotwordAlwaysOn(always_on);
+  ash::VoiceInteractionController::Get()->NotifyHotwordAlwaysOn(always_on);
 }
 
 void VoiceInteractionControllerClient::NotifyConsentStatus() {
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
-  voice_interaction_controller_->NotifyConsentStatus(
+  ash::VoiceInteractionController::Get()->NotifyConsentStatus(
       assistant::prefs::GetConsentStatus(prefs));
 }
 
@@ -136,14 +127,14 @@
   DCHECK(profile_);
   ash::mojom::AssistantAllowedState state =
       assistant::IsAssistantAllowedForProfile(profile_);
-  voice_interaction_controller_->NotifyFeatureAllowed(state);
+  ash::VoiceInteractionController::Get()->NotifyFeatureAllowed(state);
 }
 
 void VoiceInteractionControllerClient::NotifyNotificationEnabled() {
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
   bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionNotificationEnabled);
-  voice_interaction_controller_->NotifyNotificationEnabled(enabled);
+  ash::VoiceInteractionController::Get()->NotifyNotificationEnabled(enabled);
 }
 
 void VoiceInteractionControllerClient::NotifyLocaleChanged() {
@@ -154,7 +145,7 @@
   std::string out_locale =
       profile_->GetPrefs()->GetString(language::prefs::kApplicationLocale);
 
-  voice_interaction_controller_->NotifyLocaleChanged(out_locale);
+  ash::VoiceInteractionController::Get()->NotifyLocaleChanged(out_locale);
 }
 
 void VoiceInteractionControllerClient::NotifyLaunchWithMicOpen() {
@@ -162,7 +153,8 @@
   PrefService* prefs = profile_->GetPrefs();
   bool voice_preferred =
       prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen);
-  voice_interaction_controller_->NotifyLaunchWithMicOpen(voice_preferred);
+  ash::VoiceInteractionController::Get()->NotifyLaunchWithMicOpen(
+      voice_preferred);
 }
 
 void VoiceInteractionControllerClient::ActiveUserChanged(
@@ -266,18 +258,10 @@
   }
 }
 
-void VoiceInteractionControllerClient::ConnectToVoiceInteractionController() {
-  content::ServiceManagerConnection* connection =
-      content::ServiceManagerConnection::GetForProcess();
-  // Tests may bind to their own VoiceInteractionController later.
-  if (connection)
-    connection->GetConnector()->BindInterface(ash::mojom::kServiceName,
-                                              &voice_interaction_controller_);
-}
-
 void VoiceInteractionControllerClient::OnArcPlayStoreEnabledChanged(
     bool enabled) {
-  voice_interaction_controller_->NotifyArcPlayStoreEnabledChanged(enabled);
+  ash::VoiceInteractionController::Get()->NotifyArcPlayStoreEnabledChanged(
+      enabled);
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
index ee50a73..d0dd3ab4 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
@@ -46,11 +46,6 @@
     return voice_interaction_state_;
   }
 
-  // Testing methods.
-  void SetControllerForTesting(
-      ash::mojom::VoiceInteractionControllerPtr controller);
-  void FlushMojoForTesting();
-
  private:
   friend class VoiceInteractionControllerClientTest;
 
@@ -78,10 +73,6 @@
 
   void SetProfile(Profile* profile);
 
-  void ConnectToVoiceInteractionController();
-
-  ash::mojom::VoiceInteractionControllerPtr voice_interaction_controller_;
-
   content::NotificationRegistrar notification_registrar_;
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
   std::unique_ptr<user_manager::ScopedUserSessionStateObserver>
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
index 5287eec..02ea954 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
@@ -4,11 +4,10 @@
 
 #include "chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h"
 
-#include "ash/shell.h"
+#include "ash/public/cpp/voice_interaction_controller.h"
 #include "base/bind.h"
 #include "base/files/scoped_temp_dir.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
-#include "chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ui/ash/assistant/assistant_pref_util.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
@@ -49,18 +48,12 @@
     GetFakeUserManager()->AddUser(account_id);
     GetFakeUserManager()->LoginUser(account_id);
 
-    voice_interaction_controller_ =
-        std::make_unique<FakeVoiceInteractionController>();
-
     voice_interaction_controller_client_ =
         std::make_unique<VoiceInteractionControllerClient>();
-    voice_interaction_controller_client_->SetControllerForTesting(
-        voice_interaction_controller_->CreateInterfacePtrAndBind());
     voice_interaction_controller_client_->SetProfile(profile_.get());
   }
 
   void TearDown() override {
-    voice_interaction_controller_.reset();
     voice_interaction_controller_client_.reset();
     profile_.reset();
     arc_session_manager_->Shutdown();
@@ -68,10 +61,6 @@
     ChromeAshTestBase::TearDown();
   }
 
-  FakeVoiceInteractionController* voice_interaction_controller() {
-    return voice_interaction_controller_.get();
-  }
-
   VoiceInteractionControllerClient* voice_interaction_controller_client() {
     return voice_interaction_controller_client_.get();
   }
@@ -82,10 +71,6 @@
     return arc_session_manager_.get();
   }
 
-  void FlushVoiceInteractionControllerMojo() {
-    voice_interaction_controller_client()->FlushMojoForTesting();
-  }
-
  private:
   chromeos::FakeChromeUserManager* GetFakeUserManager() const {
     return static_cast<chromeos::FakeChromeUserManager*>(
@@ -96,7 +81,6 @@
   std::unique_ptr<TestingProfile> profile_;
   user_manager::ScopedUserManager fake_user_manager_;
   std::unique_ptr<ArcSessionManager> arc_session_manager_;
-  std::unique_ptr<FakeVoiceInteractionController> voice_interaction_controller_;
   std::unique_ptr<VoiceInteractionControllerClient>
       voice_interaction_controller_client_;
 };
@@ -107,26 +91,17 @@
   ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionEnabled));
   prefs->SetBoolean(prefs::kVoiceInteractionEnabled, true);
   ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionEnabled));
-  voice_interaction_controller_client()->FlushMojoForTesting();
-  EXPECT_EQ(
-      true,
-      voice_interaction_controller()->voice_interaction_settings_enabled());
+  EXPECT_EQ(true, ash::VoiceInteractionController::Get()->settings_enabled());
 
   ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionContextEnabled));
   prefs->SetBoolean(prefs::kVoiceInteractionContextEnabled, true);
   ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionContextEnabled));
-  voice_interaction_controller_client()->FlushMojoForTesting();
-  EXPECT_EQ(
-      true,
-      voice_interaction_controller()->voice_interaction_context_enabled());
+  EXPECT_EQ(true, ash::VoiceInteractionController::Get()->context_enabled());
 
   ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled));
   prefs->SetBoolean(prefs::kVoiceInteractionHotwordEnabled, true);
   ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled));
-  voice_interaction_controller_client()->FlushMojoForTesting();
-  EXPECT_EQ(
-      true,
-      voice_interaction_controller()->voice_interaction_hotword_enabled());
+  EXPECT_EQ(true, ash::VoiceInteractionController::Get()->hotword_enabled());
 
   // Default setting is true.
   ASSERT_EQ(true,
@@ -134,10 +109,8 @@
   prefs->SetBoolean(prefs::kVoiceInteractionNotificationEnabled, false);
   ASSERT_EQ(false,
             prefs->GetBoolean(prefs::kVoiceInteractionNotificationEnabled));
-  voice_interaction_controller_client()->FlushMojoForTesting();
-  EXPECT_EQ(
-      false,
-      voice_interaction_controller()->voice_interaction_notification_enabled());
+  EXPECT_EQ(false,
+            ash::VoiceInteractionController::Get()->notification_enabled());
 
   ASSERT_EQ(static_cast<int>(ash::mojom::ConsentStatus::kUnknown),
             prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
@@ -147,22 +120,20 @@
   ASSERT_EQ(
       static_cast<int>(ash::mojom::ConsentStatus::kActivityControlAccepted),
       prefs->GetInteger(assistant::prefs::kAssistantConsentStatus));
-  voice_interaction_controller_client()->FlushMojoForTesting();
   EXPECT_EQ(ash::mojom::ConsentStatus::kActivityControlAccepted,
-            voice_interaction_controller()->voice_interaction_consent_status());
+            ash::VoiceInteractionController::Get()->consent_status());
 
   ASSERT_EQ("", prefs->GetString(language::prefs::kApplicationLocale));
   prefs->SetString(language::prefs::kApplicationLocale, "en-CA");
   ASSERT_EQ("en-CA", prefs->GetString(language::prefs::kApplicationLocale));
-  voice_interaction_controller_client()->FlushMojoForTesting();
-  EXPECT_EQ("en-CA", voice_interaction_controller()->locale());
+  EXPECT_EQ("en-CA", ash::VoiceInteractionController::Get()->locale());
 
   ASSERT_EQ(false,
             prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen));
   prefs->SetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen, true);
   ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen));
-  voice_interaction_controller_client()->FlushMojoForTesting();
-  EXPECT_EQ(true, voice_interaction_controller()->launch_with_mic_open());
+  EXPECT_EQ(true,
+            ash::VoiceInteractionController::Get()->launch_with_mic_open());
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index b332d3c..d634079 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -35,6 +35,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "components/prefs/pref_service.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -213,6 +214,17 @@
     return false;
   }
 
+  bool kernelnext = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      chromeos::switches::kKernelnextRestrictVMs);
+  bool kernelnext_override =
+      base::FeatureList::IsEnabled(features::kKernelnextVMs);
+  if (kernelnext && !kernelnext_override) {
+    // The host kernel is on an experimental version. In future updates this
+    // device may not have VM support, so we allow enabling VMs, but guard them
+    // on a chrome://flags switch (enable-experimental-kernel-vm-support).
+    return false;
+  }
+
   return base::FeatureList::IsEnabled(features::kCrostini);
 }
 
diff --git a/chrome/browser/chromeos/extensions/autotest_private/OWNERS b/chrome/browser/chromeos/extensions/autotest_private/OWNERS
index c345b9b..eb940ade 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/OWNERS
+++ b/chrome/browser/chromeos/extensions/autotest_private/OWNERS
@@ -1,3 +1,2 @@
 achuith@chromium.org
-derat@chromium.org
 stevenjb@chromium.org
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
index 07cf5015..bd19f8b 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
@@ -7,7 +7,6 @@
 #include <vector>
 
 #include "base/logging.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/identity/public/cpp/accounts_cookie_mutator.h"
 
diff --git a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
index a1802d8b..f125acf 100644
--- a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
@@ -48,6 +48,7 @@
 constexpr char kCRDResponseConnect[] = "connectResponse";
 constexpr char kCRDStateChanged[] = "hostStateChanged";
 constexpr char kCRDResponseDisconnect[] = "disconnectResponse";
+constexpr char kCRDResponseError[] = "error";
 
 // Connect message parameters:
 constexpr char kCRDConnectUserName[] = "userName";
@@ -359,7 +360,7 @@
   } else if (type == kCRDResponseDisconnect) {
     OnDisconnectResponse();
     return;
-  } else if (type == kCRDStateChanged) {
+  } else if (type == kCRDStateChanged || type == kCRDResponseError) {
     // Handle CRD host state changes
     auto* state_value =
         message_value->FindKeyOfType(kCRDStateKey, base::Value::Type::STRING);
@@ -385,7 +386,7 @@
     }
     return;
   }
-  LOG(WARNING) << "Unknown message type :" << type;
+  LOG(WARNING) << "Unknown message type: " << type;
 }
 
 void CRDHostDelegate::OnHelloResponse() {
diff --git a/chrome/browser/chromeos/power/ml/OWNERS b/chrome/browser/chromeos/power/ml/OWNERS
index 18f8873..0b16b90 100644
--- a/chrome/browser/chromeos/power/ml/OWNERS
+++ b/chrome/browser/chromeos/power/ml/OWNERS
@@ -1,4 +1,3 @@
 amoylan@chromium.org
-derat@chromium.org
 jiameng@chromium.org
 martis@chromium.org
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index c314e103..53b4989 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -2116,7 +2116,7 @@
   ~StaticURLDataSource() override = default;
 
   // content::URLDataSource:
-  std::string GetSource() const override { return source_; }
+  std::string GetSource() override { return source_; }
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
@@ -2124,10 +2124,10 @@
     std::string data(content_);
     callback.Run(base::RefCountedString::TakeString(&data));
   }
-  std::string GetMimeType(const std::string& path) const override {
+  std::string GetMimeType(const std::string& path) override {
     return "text/html";
   }
-  bool ShouldAddContentSecurityPolicy() const override { return false; }
+  bool ShouldAddContentSecurityPolicy() override { return false; }
 
  private:
   const std::string source_;
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index 2aa77d1..f5b38d07 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -121,6 +121,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
 #include "net/test/url_request/url_request_mock_http_job.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/device/public/mojom/constants.mojom.h"
@@ -207,15 +208,14 @@
 
 class OnCanDownloadDecidedObserver {
  public:
-  OnCanDownloadDecidedObserver()
-      : on_decided_called_(false), last_allow_(false) {}
+  OnCanDownloadDecidedObserver() = default;
 
-  void Wait(bool expectation) {
-    if (on_decided_called_) {
-      EXPECT_EQ(last_allow_, expectation);
-      on_decided_called_ = false;
+  void Wait(const std::vector<bool>& expected_decisions) {
+    if (expected_decisions.size() <= decisions_.size()) {
+      EXPECT_TRUE(std::equal(expected_decisions.begin(),
+                             expected_decisions.end(), decisions_.begin()));
     } else {
-      expectation_ = expectation;
+      expected_decisions_ = expected_decisions;
       base::RunLoop run_loop;
       completion_closure_ = run_loop.QuitClosure();
       run_loop.Run();
@@ -223,22 +223,24 @@
   }
 
   void OnCanDownloadDecided(bool allow) {
-    // It is possible this is called before Wait(), so the result needs to
-    // be stored in that case.
-    if (!completion_closure_.is_null()) {
+    decisions_.push_back(allow);
+    if (decisions_.size() == expected_decisions_.size()) {
+      DCHECK(!completion_closure_.is_null());
+      EXPECT_EQ(decisions_, expected_decisions_);
       std::move(completion_closure_).Run();
-      EXPECT_EQ(allow, expectation_);
-    } else {
-      on_decided_called_ = true;
-      last_allow_ = allow;
     }
   }
 
+  void Reset() {
+    decisions_.clear();
+    expected_decisions_.clear();
+    completion_closure_.Reset();
+  }
+
  private:
-  bool expectation_;
+  std::vector<bool> decisions_;
+  std::vector<bool> expected_decisions_;
   base::Closure completion_closure_;
-  bool on_decided_called_;
-  bool last_allow_;
 
   DISALLOW_COPY_AND_ASSIGN(OnCanDownloadDecidedObserver);
 };
@@ -1483,7 +1485,7 @@
 }
 
 // Test to make sure 'download' attribute in anchor tag doesn't trigger a
-// downloadd if DownloadRequestLimiter disallows it.
+// download if DownloadRequestLimiter disallows it.
 IN_PROC_BROWSER_TEST_F(DownloadTest,
                        DownloadRequestLimiterDisallowsAnchorDownloadTag) {
   OnCanDownloadDecidedObserver can_download_observer;
@@ -1515,7 +1517,8 @@
       "window.domAutomationController.send(startDownload1());",
       &download_attempted));
   ASSERT_TRUE(download_attempted);
-  can_download_observer.Wait(false);
+  can_download_observer.Wait({false});
+  can_download_observer.Reset();
 
   // Let the 2nd download to succeed.
   std::unique_ptr<content::DownloadTestObserver> observer(
@@ -1527,7 +1530,7 @@
       "window.domAutomationController.send(startDownload2());",
       &download_attempted));
   ASSERT_TRUE(download_attempted);
-  can_download_observer.Wait(true);
+  can_download_observer.Wait({true});
   // Waits for the 2nd download to complete.
   observer->WaitForFinished();
 
@@ -3256,6 +3259,52 @@
   browser()->tab_strip_model()->GetActiveWebContents()->Close();
 }
 
+// Test the scenario for 3 consecutive <a download> download attempts that all
+// trigger a x-origin redirect to another download. No download is expected to
+// happen.
+IN_PROC_BROWSER_TEST_F(
+    DownloadTest,
+    MultipleAnchorDownloadsRequestsCrossOriginRedirectToAnotherDownload) {
+  std::unique_ptr<content::DownloadTestObserver> downloads_observer(
+      CreateWaiter(browser(), 0u));
+
+  OnCanDownloadDecidedObserver can_download_observer;
+  g_browser_process->download_request_limiter()
+      ->SetOnCanDownloadDecidedCallbackForTesting(base::BindRepeating(
+          &OnCanDownloadDecidedObserver::OnCanDownloadDecided,
+          base::Unretained(&can_download_observer)));
+
+  embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url = embedded_test_server()->GetURL(
+      "/downloads/multiple_a_download_x_origin_redirect_to_download.html");
+
+  base::StringPairs port_replacement;
+  port_replacement.push_back(std::make_pair(
+      "{{PORT}}", base::NumberToString(embedded_test_server()->port())));
+  std::string download_url = net::test_server::GetFilePathWithReplacements(
+      "redirect_x_origin_download.html", port_replacement);
+
+  url = GURL(url.spec() + "?download_url=" + download_url);
+
+  // Navigate to a page that triggers 3 consecutive <a download> download
+  // attempts that all trigger a x-origin redirect to another download.
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 1);
+
+  // The 1st <a download> attempt should pass the download limiter check,
+  // and prevent further download attempts from passing. The subsequent 2nd/3rd
+  // <a download> attempts as well as the |download as a result of the x-origin
+  // redirect from the 1st download attempt| should all fail the download
+  // limiter check.
+  can_download_observer.Wait({true, false, false, false});
+
+  // Only the 1st <a download> attempt passed the download limiter check, but it
+  // was still aborted by a x-origin redirect, therefore we expect no download
+  // to happen.
+  EXPECT_EQ(
+      0u, downloads_observer->NumDownloadsSeenInState(DownloadItem::COMPLETE));
+}
+
 IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadTest_Renaming) {
   embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory());
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/download/download_request_limiter.cc b/chrome/browser/download/download_request_limiter.cc
index 75cb0fa..379d7d2 100644
--- a/chrome/browser/download/download_request_limiter.cc
+++ b/chrome/browser/download/download_request_limiter.cc
@@ -151,9 +151,11 @@
       return;
     }
 
-    // If this is a forward/back navigation, also don't reset a prompting or
-    // blocking limiter state unless a new host is encounted. This prevents a
-    // page to use history forward/backward to trigger multiple downloads.
+    // If this is a navigation as a result of x-origin redirect from a previous
+    // <a download> download, or if this is a forward/back navigation in a host
+    // already seen, also don't reset a prompting or blocking limiter state.
+    // This prevents a page to use any of those mechanisms to trigger multiple
+    // downloads.
     if (IsNavigationRestricted(navigation_handle))
       return;
   }
@@ -418,6 +420,9 @@
 
 bool DownloadRequestLimiter::TabDownloadState::IsNavigationRestricted(
     content::NavigationHandle* navigation_handle) {
+  if (navigation_handle->FromDownloadCrossOriginRedirect())
+    return true;
+
   std::string host = navigation_handle->GetURL().host();
   if (navigation_handle->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK)
     return restricted_hosts_.find(host) != restricted_hosts_.end();
@@ -585,6 +590,7 @@
         case CONTENT_SETTING_ASK:
           state->PromptUserForDownload(std::move(callback));
           state->increment_download_count();
+          ret = false;
           break;
         case CONTENT_SETTING_SESSION_ONLY:
         case CONTENT_SETTING_NUM_SETTINGS:
diff --git a/chrome/browser/download/download_request_limiter.h b/chrome/browser/download/download_request_limiter.h
index 030f03e..9b00b28 100644
--- a/chrome/browser/download/download_request_limiter.h
+++ b/chrome/browser/download/download_request_limiter.h
@@ -234,6 +234,9 @@
   FRIEND_TEST_ALL_PREFIXES(DownloadTest, DownloadResourceThrottleCancels);
   FRIEND_TEST_ALL_PREFIXES(DownloadTest,
                            DownloadRequestLimiterDisallowsAnchorDownloadTag);
+  FRIEND_TEST_ALL_PREFIXES(
+      DownloadTest,
+      MultipleAnchorDownloadsRequestsCrossOriginRedirectToAnotherDownload);
   FRIEND_TEST_ALL_PREFIXES(ContentSettingBubbleControllerTest, Init);
   FRIEND_TEST_ALL_PREFIXES(ContentSettingImageModelBrowserTest,
                            CreateBubbleModel);
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc b/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc
index 5e1ea66b..5556209 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc
@@ -10,6 +10,8 @@
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/feedback/feedback_dialog_utils.h"
+#include "chrome/browser/feedback/feedback_uploader_chrome.h"
+#include "chrome/browser/feedback/feedback_uploader_factory_chrome.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -94,6 +96,18 @@
   }
 };
 
+class TestFeedbackUploaderDelegate
+    : public feedback::FeedbackUploaderChrome::Delegate {
+ public:
+  explicit TestFeedbackUploaderDelegate(base::RunLoop* quit_on_dispatch)
+      : quit_on_dispatch_(quit_on_dispatch) {}
+
+  void OnStartDispatchingReport() override { quit_on_dispatch_->Quit(); }
+
+ private:
+  base::RunLoop* quit_on_dispatch_;
+};
+
 // Disabled for ASan due to flakiness on Mac ASan 64 Tests (1).
 // See crbug.com/757243.
 #if defined(ADDRESS_SANITIZER)
@@ -303,7 +317,7 @@
   EXPECT_TRUE(bool_result);
 }
 #endif  // if defined(CHROME_OS)
-  
+
 IN_PROC_BROWSER_TEST_F(FeedbackTest, GetTargetTabUrl) {
   const std::pair<std::string, std::string> test_cases[] = {
       {"https://www.google.com/", "https://www.google.com/"},
@@ -340,4 +354,49 @@
     DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
   }
 }
+
+IN_PROC_BROWSER_TEST_F(FeedbackTest, SubmissionTest) {
+  WaitForExtensionViewsToLoad();
+
+  ASSERT_TRUE(IsFeedbackAppAvailable());
+  StartFeedbackUI(FeedbackFlow::FEEDBACK_FLOW_GOOGLEINTERNAL, std::string());
+  VerifyFeedbackAppLaunch();
+
+  AppWindow* const window =
+      PlatformAppBrowserTest::GetFirstAppWindowForBrowser(browser());
+  ASSERT_TRUE(window);
+  content::WebContents* const content = window->web_contents();
+
+  // Set a delegate for the uploader which will be invoked when the report
+  // normally would have been uploaded. We have it setup to then quit the
+  // RunLoop which will then allow us to terminate.
+  base::RunLoop run_loop;
+  TestFeedbackUploaderDelegate delegate(&run_loop);
+  feedback::FeedbackUploaderFactoryChrome::GetInstance()
+      ->GetForBrowserContext(browser()->profile())
+      ->set_feedback_uploader_delegate(&delegate);
+
+  // Click the send button.
+  bool bool_result = false;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      content,
+      "domAutomationController.send("
+      "  ((function() {"
+      "      if ($('send-report-button') != null) {"
+      "        document.getElementById('send-report-button').click();"
+      "        return true;"
+      "      }"
+      "      return false;"
+      "    })()));",
+      &bool_result));
+  EXPECT_TRUE(bool_result);
+
+  // This will DCHECK if the JS private API call doesn't return a value, which
+  // is the main case we are concerned about.
+  run_loop.Run();
+  feedback::FeedbackUploaderFactoryChrome::GetInstance()
+      ->GetForBrowserContext(browser()->profile())
+      ->set_feedback_uploader_delegate(nullptr);
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/process_manager_browsertest.cc b/chrome/browser/extensions/process_manager_browsertest.cc
index ff003385..cd2a23d5 100644
--- a/chrome/browser/extensions/process_manager_browsertest.cc
+++ b/chrome/browser/extensions/process_manager_browsertest.cc
@@ -915,14 +915,6 @@
   }
 }
 
-// Flaky on Win, Mac and Linux (http://crbug.com/806684).
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
-#define MAYBE_NestedURLDownloadsToExtensionAllowed \
-  DISABLED_NestedURLDownloadsToExtensionAllowed
-#else
-#define MAYBE_NestedURLDownloadsToExtensionAllowed \
-  NestedURLDownloadsToExtensionAllowed
-#endif
 // Check that browser-side restrictions on extension blob/filesystem URLs allow
 // navigations that will result in downloads.  See https://crbug.com/714373.
 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
diff --git a/chrome/browser/feedback/feedback_uploader_chrome.cc b/chrome/browser/feedback/feedback_uploader_chrome.cc
index 37653a1d..f0f4f14 100644
--- a/chrome/browser/feedback/feedback_uploader_chrome.cc
+++ b/chrome/browser/feedback/feedback_uploader_chrome.cc
@@ -46,6 +46,9 @@
 }
 
 void FeedbackUploaderChrome::StartDispatchingReport() {
+  if (delegate_)
+    delegate_->OnStartDispatchingReport();
+
   access_token_.clear();
 
   // TODO(crbug.com/849591): Instead of getting the IdentityManager from the
diff --git a/chrome/browser/feedback/feedback_uploader_chrome.h b/chrome/browser/feedback/feedback_uploader_chrome.h
index e26eaf0..5a49d651 100644
--- a/chrome/browser/feedback/feedback_uploader_chrome.h
+++ b/chrome/browser/feedback/feedback_uploader_chrome.h
@@ -28,6 +28,19 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   ~FeedbackUploaderChrome() override;
 
+  class Delegate {
+   public:
+    // Notifies the delegate when we have started dispatching a feedback report.
+    virtual void OnStartDispatchingReport() = 0;
+
+   protected:
+    virtual ~Delegate() = default;
+  };
+
+  void set_feedback_uploader_delegate(Delegate* delegate) {
+    delegate_ = delegate;
+  }
+
  private:
   // feedback::FeedbackUploader:
   void StartDispatchingReport() override;
@@ -41,6 +54,8 @@
 
   std::string access_token_;
 
+  Delegate* delegate_ = nullptr;  // Not owned.
+
   DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderChrome);
 };
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 5e8395e..46ab16d 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1066,6 +1066,11 @@
     "expiry_milestone": 78
   },
   {
+    "name": "enable-experimental-kernel-vm-support",
+    "owners": [ "jflat", "zwisler" ],
+    "expiry_milestone": 78
+  },
+  {
     "name": "enable-experimental-productivity-features",
     "owners": [ "feature-control@chromium.org" ],
     "expiry_milestone": 76
@@ -2195,8 +2200,12 @@
   },
   {
     "name": "media-router-cast-allow-all-ips",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "mfoltz" ],
+    // This flag is used by users with unusual network configurations to allow
+    // cast to work, but enabling this behavior has security implications that
+    // aren't easily understood. It is primarily used by support to help
+    // individual users get cast working.
+    "expiry_milestone": -1
   },
   {
     "name": "memlog",
@@ -2717,11 +2726,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "show-managed-ui",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "show-taps",
     "owners": [ "//ash/OWNERS" ],
     // This is a debug flag, so that video bug reports can show input taps to
diff --git a/chrome/browser/flag-never-expire-list.json b/chrome/browser/flag-never-expire-list.json
index df1a9e7..7122317 100644
--- a/chrome/browser/flag-never-expire-list.json
+++ b/chrome/browser/flag-never-expire-list.json
@@ -64,6 +64,7 @@
   "in-product-help-demo-mode-choice",
   "list-all-display-modes",
   "load-media-router-component-extension",
+  "media-router-cast-allow-all-ips",
   "memlog",
   "memlog-sampling-rate",
   "memlog-stack-mode",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 99f90cc..74a3bb68 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2586,6 +2586,10 @@
     "When going to a site that has URL managable by a PWA, show the intent"
     "picker to allow user to open the URL in the app.";
 
+const char kKernelnextVMsName[] = "Enable VMs on experimental kernels.";
+const char kKernelnextVMsDescription[] =
+    "Enables VM support on devices running experimental kernel versions.";
+
 const char kOmniboxDriveSuggestionsName[] =
     "Omnibox Google Drive Document suggestions";
 const char kOmniboxDriveSuggestionsDescriptions[] =
@@ -2643,11 +2647,6 @@
     "Enables proactive tab freezing and discarding. This requires "
     "#enable-page-almost-idle.";
 
-const char kShowManagedUiName[] = "Show managed UI for managed users";
-const char kShowManagedUiDescription[] =
-    "Enabled/disable showing enterprise users a 'Managed by your organization' "
-    "message in the app menu and on some chrome:// pages.";
-
 #if defined(GOOGLE_CHROME_BUILD)
 
 const char kGoogleBrandedContextMenuName[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index fb9da327..9ae423c 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1527,6 +1527,9 @@
 extern const char kIntentPickerName[];
 extern const char kIntentPickerDescription[];
 
+extern const char kKernelnextVMsName[];
+extern const char kKernelnextVMsDescription[];
+
 extern const char kOmniboxDriveSuggestionsName[];
 extern const char kOmniboxDriveSuggestionsDescriptions[];
 
@@ -1554,9 +1557,6 @@
 extern const char kProactiveTabFreezeAndDiscardName[];
 extern const char kProactiveTabFreezeAndDiscardDescription[];
 
-extern const char kShowManagedUiName[];
-extern const char kShowManagedUiDescription[];
-
 #if defined(GOOGLE_CHROME_BUILD)
 
 extern const char kGoogleBrandedContextMenuName[];
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index 40cc924..ecce1cd 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -570,12 +570,9 @@
   ShowAndVerifyUi();
 }
 
-// TODO(https://crbug.com/965468) Resource generation fails on Windows.
-#if !defined(OS_WIN)
 IN_PROC_BROWSER_TEST_F(InfoBarUiTest, InvokeUi_tab_sharing) {
   ShowAndVerifyUi();
 }
-#endif
 
 IN_PROC_BROWSER_TEST_F(InfoBarUiTest, InvokeUi_multiple_infobars) {
   ShowAndVerifyUi();
diff --git a/chrome/browser/media/router/test/mock_screen_availability_listener.cc b/chrome/browser/media/router/test/mock_screen_availability_listener.cc
index 091513b..374ce3e 100644
--- a/chrome/browser/media/router/test/mock_screen_availability_listener.cc
+++ b/chrome/browser/media/router/test/mock_screen_availability_listener.cc
@@ -12,7 +12,7 @@
 
 MockScreenAvailabilityListener::~MockScreenAvailabilityListener() {}
 
-GURL MockScreenAvailabilityListener::GetAvailabilityUrl() const {
+GURL MockScreenAvailabilityListener::GetAvailabilityUrl() {
   return availability_url_;
 }
 
diff --git a/chrome/browser/media/router/test/mock_screen_availability_listener.h b/chrome/browser/media/router/test/mock_screen_availability_listener.h
index ac436af9..e90b5c66 100644
--- a/chrome/browser/media/router/test/mock_screen_availability_listener.h
+++ b/chrome/browser/media/router/test/mock_screen_availability_listener.h
@@ -16,7 +16,7 @@
   explicit MockScreenAvailabilityListener(const GURL& availability_url);
   ~MockScreenAvailabilityListener() override;
 
-  GURL GetAvailabilityUrl() const override;
+  GURL GetAvailabilityUrl() override;
 
   MOCK_METHOD1(OnScreenAvailabilityChanged,
                void(blink::mojom::ScreenAvailability));
diff --git a/chrome/browser/metrics/DEPS b/chrome/browser/metrics/DEPS
index 2e11ffb..cf0b66cb 100644
--- a/chrome/browser/metrics/DEPS
+++ b/chrome/browser/metrics/DEPS
@@ -1,3 +1,10 @@
 include_rules = [
   "+chrome/services/util_win/util_win_service.h",
 ]
+
+specific_include_rules = {
+  # TODO(siggi): Fix. https://crbug.com/969026
+  "process_memory_metrics_emitter.cc": [
+    "+chrome/browser/performance_manager",
+  ],
+}
diff --git a/chrome/browser/notifications/scheduler/BUILD.gn b/chrome/browser/notifications/scheduler/BUILD.gn
index e749e31..59b3088 100644
--- a/chrome/browser/notifications/scheduler/BUILD.gn
+++ b/chrome/browser/notifications/scheduler/BUILD.gn
@@ -14,31 +14,7 @@
   ]
 
   public_deps = [
-    ":public",
-  ]
-}
-
-source_set("public") {
-  sources = [
-    "notification_background_task_scheduler.h",
-    "notification_data.cc",
-    "notification_data.h",
-    "notification_params.cc",
-    "notification_params.h",
-    "notification_schedule_service.h",
-    "notification_scheduler_client.h",
-    "notification_scheduler_client_registrar.cc",
-    "notification_scheduler_client_registrar.h",
-    "notification_scheduler_types.h",
-    "schedule_params.cc",
-    "schedule_params.h",
-    "user_action_handler.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/keyed_service/core",
-    "//skia",
+    "//chrome/browser/notifications/scheduler/public",
   ]
 }
 
@@ -51,106 +27,18 @@
 
   # This target should not depend on anything in //chrome/* except the proto library.
   deps = [
-    ":lib",
-    ":public",
     "//base",
     "//chrome/browser/notifications/proto",
+    "//chrome/browser/notifications/scheduler/internal:lib",
+    "//chrome/browser/notifications/scheduler/public",
     "//components/keyed_service/core",
     "//components/leveldb_proto",
   ]
 }
 
-# Internal library that embedders should not directly depend on.
-source_set("lib") {
-  visibility = [
-    ":factory",
-    ":scheduler",
-    ":unit_tests",
-    "//chrome/browser/notifications/scheduler/test:test_lib",
-  ]
-
-  sources = [
-    "background_task_coordinator.cc",
-    "background_task_coordinator.h",
-    "collection_store.h",
-    "display_decider.cc",
-    "display_decider.h",
-    "distribution_policy.cc",
-    "distribution_policy.h",
-    "icon_entry.cc",
-    "icon_entry.h",
-    "icon_store.cc",
-    "icon_store.h",
-    "impression_history_tracker.cc",
-    "impression_history_tracker.h",
-    "impression_store.cc",
-    "impression_store.h",
-    "impression_types.cc",
-    "impression_types.h",
-    "init_aware_scheduler.cc",
-    "init_aware_scheduler.h",
-    "notification_entry.cc",
-    "notification_entry.h",
-    "notification_schedule_service_impl.cc",
-    "notification_schedule_service_impl.h",
-    "notification_scheduler.cc",
-    "notification_scheduler.h",
-    "notification_scheduler_context.cc",
-    "notification_scheduler_context.h",
-    "notification_store.cc",
-    "notification_store.h",
-    "proto_conversion.cc",
-    "proto_conversion.h",
-    "scheduled_notification_manager.cc",
-    "scheduled_notification_manager.h",
-    "scheduler_config.cc",
-    "scheduler_config.h",
-    "scheduler_utils.cc",
-    "scheduler_utils.h",
-  ]
-
-  # This target should not depend on anything in //chrome/* except the proto library.
-  deps = [
-    ":public",
-    "//base",
-    "//chrome/browser/notifications/proto",
-    "//components/keyed_service/core",
-    "//components/leveldb_proto",
-    "//skia",
-  ]
-}
-
-source_set("unit_tests") {
+group("unit_tests") {
   testonly = true
-  sources = [
-    "background_task_coordinator_unittest.cc",
-    "display_decider_unittest.cc",
-    "distribution_policy_unittest.cc",
-    "icon_store_unittest.cc",
-    "impression_history_tracker_unittest.cc",
-    "impression_store_unittest.cc",
-    "init_aware_scheduler_unittest.cc",
-    "notification_store_unittests.cc",
-    "proto_conversion_unittest.cc",
-    "scheduled_notification_manager_unittest.cc",
-    "scheduler_utils_unittest.cc",
-  ]
-
   deps = [
-    ":lib",
-    ":public",
-    "//chrome/browser/notifications/proto",
-    "//chrome/browser/notifications/scheduler/test:test_lib",
-    "//components/leveldb_proto:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
+    "//chrome/browser/notifications/scheduler/internal:unit_tests",
   ]
 }
-
-if (is_android) {
-  java_cpp_enum("jni_enums") {
-    sources = [
-      "notification_scheduler_types.h",
-    ]
-  }
-}
diff --git a/chrome/browser/notifications/scheduler/background_task_coordinator.cc b/chrome/browser/notifications/scheduler/background_task_coordinator.cc
deleted file mode 100644
index b31c623..0000000
--- a/chrome/browser/notifications/scheduler/background_task_coordinator.cc
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/background_task_coordinator.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/numerics/ranges.h"
-#include "base/optional.h"
-#include "base/time/clock.h"
-#include "chrome/browser/notifications/scheduler/impression_types.h"
-#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
-#include "chrome/browser/notifications/scheduler/scheduler_config.h"
-#include "chrome/browser/notifications/scheduler/scheduler_utils.h"
-
-namespace notifications {
-namespace {
-
-class BackgroundTaskCoordinatorHelper {
- public:
-  BackgroundTaskCoordinatorHelper(
-      NotificationBackgroundTaskScheduler* background_task,
-      const SchedulerConfig* config,
-      base::Clock* clock)
-      : background_task_(background_task), config_(config), clock_(clock) {}
-  ~BackgroundTaskCoordinatorHelper() = default;
-
-  void ScheduleBackgroundTask(
-      BackgroundTaskCoordinator::Notifications notifications,
-      BackgroundTaskCoordinator::ClientStates client_states,
-      SchedulerTaskTime task_start_time) {
-    if (notifications.empty()) {
-      background_task_->Cancel();
-      return;
-    }
-
-    std::map<SchedulerClientType, int> shown_per_type;
-    int shown_total = 0;
-    SchedulerClientType last_shown_type = SchedulerClientType::kUnknown;
-    NotificationsShownToday(client_states, &shown_per_type, &shown_total,
-                            &last_shown_type);
-    bool reach_max_today_all_type =
-        shown_total >= config_->max_daily_shown_all_type;
-    base::Time next_morning = NextMorning();
-    base::Time this_evening = ThisEvening();
-
-    for (const auto& pair : notifications) {
-      auto type = pair.first;
-      auto it = client_states.find(type);
-      if (pair.second.empty() || (it == client_states.end()))
-        continue;
-
-      const ClientState* client_state = it->second;
-
-      // Try to schedule on the day that suppression expires.
-      if (client_state->suppression_info.has_value()) {
-        const auto& suppression = client_state->suppression_info.value();
-        base::Time suppression_expire;
-        ToLocalHour(config_->morning_task_hour, suppression.ReleaseTime(),
-                    0 /*day_delta*/, &suppression_expire);
-        MaybeUpdateBackgroundTaskTime(
-            std::max(suppression_expire, next_morning));
-        continue;
-      }
-
-      // Has met the quota for this notification type or for all types, only can
-      // send more on next day.
-      bool reach_max_today =
-          shown_per_type[type] >= client_state->current_max_daily_show;
-      if (reach_max_today || reach_max_today_all_type) {
-        MaybeUpdateBackgroundTaskTime(next_morning);
-        continue;
-      }
-
-      switch (task_start_time) {
-        case SchedulerTaskTime::kMorning:
-          // Still can send more in the evening.
-          MaybeUpdateBackgroundTaskTime(this_evening);
-          break;
-        case SchedulerTaskTime::kEvening:
-          // Wait until the next calendar day.
-          MaybeUpdateBackgroundTaskTime(next_morning);
-          break;
-        case SchedulerTaskTime::kUnknown:
-          // TODO(xingliu): Support arbitrary time background task.
-          NOTIMPLEMENTED();
-          break;
-      }
-    }
-
-    ScheduleBackgroundTaskInternal(task_start_time);
-  }
-
- private:
-  // Returns the morning background task time on the next day.
-  base::Time NextMorning() {
-    base::Time next_morning;
-    bool success = ToLocalHour(config_->morning_task_hour, clock_->Now(),
-                               1 /*day_delta*/, &next_morning);
-    DCHECK(success);
-    return next_morning;
-  }
-
-  // Returns the evening background task time on today.
-  base::Time ThisEvening() {
-    base::Time this_evening;
-    bool success = ToLocalHour(config_->evening_task_hour, clock_->Now(),
-                               0 /*day_delta*/, &this_evening);
-    DCHECK(success);
-    return this_evening;
-  }
-
-  void MaybeUpdateBackgroundTaskTime(const base::Time& time) {
-    if (!background_task_time_.has_value() ||
-        time < background_task_time_.value())
-      background_task_time_ = time;
-  }
-
-  void ScheduleBackgroundTaskInternal(SchedulerTaskTime task_start_time) {
-    if (!background_task_time_.has_value())
-      return;
-
-    base::TimeDelta window_start_time =
-        background_task_time_.value() - clock_->Now();
-    window_start_time = base::ClampToRange(window_start_time, base::TimeDelta(),
-                                           base::TimeDelta::Max());
-
-    background_task_->Schedule(
-        task_start_time, window_start_time,
-        window_start_time + config_->background_task_window_duration);
-  }
-
-  NotificationBackgroundTaskScheduler* background_task_;
-  const SchedulerConfig* config_;
-  base::Clock* clock_;
-  base::Optional<base::Time> background_task_time_;
-
-  DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorHelper);
-};
-
-}  // namespace
-
-BackgroundTaskCoordinator::BackgroundTaskCoordinator(
-    std::unique_ptr<NotificationBackgroundTaskScheduler> background_task,
-    const SchedulerConfig* config,
-    base::Clock* clock)
-    : background_task_(std::move(background_task)),
-      config_(config),
-      clock_(clock) {}
-
-BackgroundTaskCoordinator::~BackgroundTaskCoordinator() = default;
-
-void BackgroundTaskCoordinator::ScheduleBackgroundTask(
-    Notifications notifications,
-    ClientStates client_states,
-    SchedulerTaskTime task_start_time) {
-  auto helper = std::make_unique<BackgroundTaskCoordinatorHelper>(
-      background_task_.get(), config_, clock_);
-  helper->ScheduleBackgroundTask(std::move(notifications),
-                                 std::move(client_states), task_start_time);
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/background_task_coordinator.h b/chrome/browser/notifications/scheduler/background_task_coordinator.h
deleted file mode 100644
index 0487485..0000000
--- a/chrome/browser/notifications/scheduler/background_task_coordinator.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_BACKGROUND_TASK_COORDINATOR_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_BACKGROUND_TASK_COORDINATOR_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace base {
-class Clock;
-}  // namespace base
-
-namespace notifications {
-
-class NotificationBackgroundTaskScheduler;
-struct ClientState;
-struct NotificationEntry;
-struct SchedulerConfig;
-
-// Schedules background task at the right time based on scheduled notification
-// data and impression data.
-class BackgroundTaskCoordinator {
- public:
-  using Notifications =
-      std::map<SchedulerClientType, std::vector<const NotificationEntry*>>;
-  using ClientStates = std::map<SchedulerClientType, const ClientState*>;
-  BackgroundTaskCoordinator(
-      std::unique_ptr<NotificationBackgroundTaskScheduler> background_task,
-      const SchedulerConfig* config,
-      base::Clock* clock);
-  virtual ~BackgroundTaskCoordinator();
-
-  // Schedule background task based on current notification in the storage.
-  virtual void ScheduleBackgroundTask(Notifications notifications,
-                                      ClientStates client_states,
-                                      SchedulerTaskTime task_start_time);
-
- private:
-  // The class that actually schedules platform background task.
-  std::unique_ptr<NotificationBackgroundTaskScheduler> background_task_;
-
-  // System configuration.
-  const SchedulerConfig* config_;
-
-  // Clock to query the current timestamp.
-  base::Clock* clock_;
-
-  DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinator);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_BACKGROUND_TASK_COORDINATOR_H_
diff --git a/chrome/browser/notifications/scheduler/background_task_coordinator_unittest.cc b/chrome/browser/notifications/scheduler/background_task_coordinator_unittest.cc
deleted file mode 100644
index c760011c..0000000
--- a/chrome/browser/notifications/scheduler/background_task_coordinator_unittest.cc
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/background_task_coordinator.h"
-
-#include <map>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/test/scoped_task_environment.h"
-#include "base/time/clock.h"
-#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
-#include "chrome/browser/notifications/scheduler/scheduler_config.h"
-#include "chrome/browser/notifications/scheduler/test/test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-
-namespace notifications {
-namespace {
-
-using Notifications = BackgroundTaskCoordinator::Notifications;
-using ClientStates = BackgroundTaskCoordinator::ClientStates;
-
-const char kGuid[] = "1234";
-const std::vector<test::ImpressionTestData> kSingleClientImpressionTestData = {
-    {SchedulerClientType::kTest1,
-     1 /* current_max_daily_show */,
-     {},
-     base::nullopt /* suppression_info */}};
-
-const std::vector<test::ImpressionTestData> kClientsImpressionTestData = {
-    {SchedulerClientType::kTest1,
-     1 /* current_max_daily_show */,
-     {},
-     base::nullopt /* suppression_info */},
-    {SchedulerClientType::kTest2,
-     2 /* current_max_daily_show */,
-     {},
-     base::nullopt /* suppression_info */}};
-
-class MockNotificationBackgroundTaskScheduler
-    : public NotificationBackgroundTaskScheduler {
- public:
-  MockNotificationBackgroundTaskScheduler() = default;
-  ~MockNotificationBackgroundTaskScheduler() override = default;
-  MOCK_METHOD3(Schedule,
-               void(notifications::SchedulerTaskTime,
-                    base::TimeDelta,
-                    base::TimeDelta));
-  MOCK_METHOD0(Cancel, void());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockNotificationBackgroundTaskScheduler);
-};
-
-// Clock to mock Clock::Now() to get a fixed time in the test.
-class FakeClock : public base::Clock {
- public:
-  FakeClock() = default;
-  ~FakeClock() override = default;
-
-  void SetTime(const base::Time& time) { time_ = time; }
-
- private:
-  // base::Clock implementation.
-  base::Time Now() const override { return time_; }
-
-  base::Time time_;
-  DISALLOW_COPY_AND_ASSIGN(FakeClock);
-};
-
-struct TestData {
-  // Impression data as the input.
-  std::vector<test::ImpressionTestData> impression_test_data;
-
-  // Notification entries as the input.
-  std::vector<NotificationEntry> notification_entries;
-
-  // The type of current background task.
-  SchedulerTaskTime task_start_time = SchedulerTaskTime::kMorning;
-};
-
-class BackgroundTaskCoordinatorTest : public testing::Test {
- public:
-  BackgroundTaskCoordinatorTest() = default;
-  ~BackgroundTaskCoordinatorTest() override = default;
-
- protected:
-  void SetUp() override {
-    // Setup configuration used by this test.
-    config_.morning_task_hour = 6;
-    config_.evening_task_hour = 18;
-    config_.max_daily_shown_all_type = 3;
-    config_.max_daily_shown_per_type = 2;
-    config_.suppression_duration = base::TimeDelta::FromDays(3);
-
-    auto background_task =
-        std::make_unique<MockNotificationBackgroundTaskScheduler>();
-    background_task_ = background_task.get();
-    coordinator_ = std::make_unique<BackgroundTaskCoordinator>(
-        std::move(background_task), &config_, &clock_);
-  }
-
-  MockNotificationBackgroundTaskScheduler* background_task() {
-    return background_task_;
-  }
-
-  SchedulerConfig* config() { return &config_; }
-
-  void SetNow(const char* now_str) {
-    base::Time now = GetTime(now_str);
-    clock_.SetTime(now);
-  }
-
-  base::Time GetTime(const char* time_str) {
-    base::Time time;
-    bool success = base::Time::FromString(time_str, &time);
-    DCHECK(success);
-    return time;
-  }
-
-  void ScheduleTask(const TestData& test_data) {
-    test_data_ = test_data;
-    test::AddImpressionTestData(test_data_.impression_test_data,
-                                &client_states_);
-    std::map<SchedulerClientType, const ClientState*> client_states;
-    for (const auto& type : client_states_) {
-      client_states.emplace(type.first, type.second.get());
-    }
-
-    Notifications notifications;
-    for (const auto& entry : test_data_.notification_entries) {
-      notifications[entry.type].emplace_back(&entry);
-    }
-    coordinator_->ScheduleBackgroundTask(std::move(notifications),
-                                         std::move(client_states),
-                                         test_data_.task_start_time);
-  }
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  FakeClock clock_;
-  SchedulerConfig config_;
-  std::unique_ptr<BackgroundTaskCoordinator> coordinator_;
-  MockNotificationBackgroundTaskScheduler* background_task_;
-  TestData test_data_;
-  std::map<SchedulerClientType, std::unique_ptr<ClientState>> client_states_;
-
-  DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorTest);
-};
-
-// No notification persisted, then no background task needs to be scheduled.
-// And current task should be canceled.
-TEST_F(BackgroundTaskCoordinatorTest, NoNotification) {
-  EXPECT_CALL(*background_task(), Cancel());
-  EXPECT_CALL(*background_task(), Schedule(_, _, _)).Times(0);
-  TestData test_data;
-  test_data.impression_test_data = kSingleClientImpressionTestData;
-  ScheduleTask(test_data);
-}
-
-// In a morning task, find one notification and schedule an evening task.
-TEST_F(BackgroundTaskCoordinatorTest, InMorningScheduleEvening) {
-  const char kNow[] = "04/25/20 01:00:00 AM";
-  SetNow(kNow);
-  EXPECT_CALL(*background_task(), Cancel()).Times(0);
-  // Expected to run task this evening.
-  auto expected_window_start = GetTime("04/25/20 18:00:00 PM") - GetTime(kNow);
-  EXPECT_CALL(*background_task(),
-              Schedule(_, expected_window_start,
-                       expected_window_start +
-                           config()->background_task_window_duration));
-
-  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
-  TestData test_data{
-      kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kMorning};
-  ScheduleTask(test_data);
-}
-
-// In morning task, schedule evening task but throttled, schedule to next
-// morning.
-TEST_F(BackgroundTaskCoordinatorTest, InMorningScheduleEveningThrottled) {
-  const char kNow[] = "04/25/20 02:00:00 PM";
-  SetNow(kNow);
-
-  EXPECT_CALL(*background_task(), Cancel()).Times(0);
-  // Expected to run task next morning.
-  EXPECT_CALL(*background_task(),
-              Schedule(_, GetTime("04/26/20 06:00:00 AM") - GetTime(kNow), _));
-
-  auto impression_data = kSingleClientImpressionTestData;
-  Impression impression;
-  impression.create_time = GetTime("04/25/20 01:00:00 AM");
-  impression_data.back().impressions.emplace_back(impression);
-
-  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
-  TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning};
-  ScheduleTask(test_data);
-}
-
-// In an evening task, schedule background task to run next morning.
-TEST_F(BackgroundTaskCoordinatorTest, InEveningScheduleNextMorning) {
-  const char kNow[] = "04/25/20 18:00:00 PM";
-  SetNow(kNow);
-  EXPECT_CALL(*background_task(), Cancel()).Times(0);
-  // Expected to run task next morning.
-  auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow);
-  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
-
-  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
-  TestData test_data{
-      kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kEvening};
-  ScheduleTask(test_data);
-}
-
-// In an evening task, schedule background task to run next morning, even if we
-// reached the daily max.
-TEST_F(BackgroundTaskCoordinatorTest, InEveningScheduleNextMorningThrottled) {
-  const char kNow[] = "04/25/20 18:00:00 PM";
-  SetNow(kNow);
-  EXPECT_CALL(*background_task(), Cancel()).Times(0);
-  // Expected to run task next morning.
-  auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow);
-  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
-
-  // We have reached daily max.
-  auto impression_data = kSingleClientImpressionTestData;
-  Impression impression;
-  impression.create_time = GetTime("04/25/20 01:00:00 AM");
-  impression_data.back().impressions.emplace_back(impression);
-
-  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
-  TestData test_data{
-      kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kEvening};
-  ScheduleTask(test_data);
-}
-
-// Suppression will result in background task scheduled after suppression
-// expired.
-TEST_F(BackgroundTaskCoordinatorTest, Suppression) {
-  const char kNow[] = "04/25/20 06:00:00 AM";
-  SetNow(kNow);
-  EXPECT_CALL(*background_task(), Cancel()).Times(0);
-  // Expected to run task in the morning after suppression expired.
-  auto expected_window_start = GetTime("04/28/20 06:00:00 AM") - GetTime(kNow);
-  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
-
-  auto impression_data = kSingleClientImpressionTestData;
-  impression_data.back().suppression_info = SuppressionInfo(
-      GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3));
-
-  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
-  TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning};
-  ScheduleTask(test_data);
-}
-
-// If two different types want to schedule at different times, pick the earilier
-// one.
-TEST_F(BackgroundTaskCoordinatorTest, ScheduleEarlierTime) {
-  const char kNow[] = "04/25/20 01:00:00 AM";
-  SetNow(kNow);
-  EXPECT_CALL(*background_task(), Cancel()).Times(0);
-  // kTest1 type will run this evening, kTest2 will run task 3 days later.
-  // Expected to run the earilier task.
-  auto expected_window_start = GetTime("04/25/20 18:00:00 PM") - GetTime(kNow);
-  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
-
-  NotificationEntry entry1(SchedulerClientType::kTest1, kGuid);
-  NotificationEntry entry2(SchedulerClientType::kTest2, "guid_entry2");
-  auto impression_data = kClientsImpressionTestData;
-  impression_data[0].suppression_info = SuppressionInfo(
-      GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3));
-  TestData test_data{
-      impression_data, {entry1, entry2}, SchedulerTaskTime::kMorning};
-  ScheduleTask(test_data);
-}
-
-// If reached |max_daily_shown_all_type|, background task should run tomorrow.
-TEST_F(BackgroundTaskCoordinatorTest, InMorningThrottledAllTypes) {
-  const char kNow[] = "04/25/20 05:00:00 AM";
-  SetNow(kNow);
-  EXPECT_CALL(*background_task(), Cancel()).Times(0);
-  // Expected to run task next morning.
-  auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow);
-  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
-
-  auto impression_data = kClientsImpressionTestData;
-  Impression impression;
-  impression.create_time = GetTime("04/25/20 01:00:00 AM");
-
-  // Make sure we reach daily max for all types.
-  for (int i = 0; i < config()->max_daily_shown_all_type; i++)
-    impression_data.back().impressions.emplace_back(impression);
-
-  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
-  TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning};
-  ScheduleTask(test_data);
-}
-
-// If reached |max_daily_shown_all_type| and all types have suppression,
-// background task should run after one suppression expired.
-TEST_F(BackgroundTaskCoordinatorTest, ThrottledAllTypesAndSuppression) {
-  const char kNow[] = "04/25/20 05:00:00 AM";
-  SetNow(kNow);
-  EXPECT_CALL(*background_task(), Cancel()).Times(0);
-  // Expected to run after 3 days suppression ends.
-  auto expected_window_start = GetTime("04/28/20 06:00:00 AM") - GetTime(kNow);
-  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
-
-  auto impression_data = kClientsImpressionTestData;
-  Impression impression;
-  impression.create_time = GetTime("04/25/20 01:00:00 AM");
-
-  // Make sure we reach daily max for all types.
-  for (int i = 0; i < config()->max_daily_shown_all_type; i++)
-    impression_data[1].impressions.emplace_back(impression);
-
-  // Suppression for both types.
-  impression_data[0].suppression_info = SuppressionInfo(
-      GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3));
-  impression_data[1].suppression_info = SuppressionInfo(
-      GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(4));
-
-  NotificationEntry entry1(SchedulerClientType::kTest1, "test_guid_1");
-  NotificationEntry entry2(SchedulerClientType::kTest2, "test_guid_2");
-  TestData test_data{
-      impression_data, {entry1, entry2}, SchedulerTaskTime::kMorning};
-  ScheduleTask(test_data);
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/collection_store.h b/chrome/browser/notifications/scheduler/collection_store.h
deleted file mode 100644
index 25543c8..0000000
--- a/chrome/browser/notifications/scheduler/collection_store.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-
-namespace notifications {
-
-// A storage interface which loads a collection of data type T into memory
-// during initialization. When updating the data, T will be copied to the actual
-// storage layer since the caller will keep in memory data as well.
-template <typename T>
-class CollectionStore {
- public:
-  using Entries = std::vector<std::unique_ptr<T>>;
-  using LoadCallback = base::OnceCallback<void(bool, Entries)>;
-  using InitCallback = base::OnceCallback<void(bool)>;
-  using UpdateCallback = base::OnceCallback<void(bool)>;
-
-  // Initializes the database and loads all entries into memory.
-  virtual void InitAndLoad(LoadCallback callback) = 0;
-
-  // Adds an entry to the storage.
-  virtual void Add(const std::string& key,
-                   const T& entry,
-                   UpdateCallback callback) = 0;
-
-  // Updates an entry.
-  virtual void Update(const std::string& key,
-                      const T& entry,
-                      UpdateCallback callback) = 0;
-
-  // Deletes an entry from storage.
-  virtual void Delete(const std::string& key, UpdateCallback callback) = 0;
-
-  CollectionStore() = default;
-  virtual ~CollectionStore() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(CollectionStore);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/display_decider.cc b/chrome/browser/notifications/scheduler/display_decider.cc
deleted file mode 100644
index 91759454..0000000
--- a/chrome/browser/notifications/scheduler/display_decider.cc
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/display_decider.h"
-
-#include <algorithm>
-
-#include "chrome/browser/notifications/scheduler/distribution_policy.h"
-#include "chrome/browser/notifications/scheduler/impression_types.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
-#include "chrome/browser/notifications/scheduler/scheduler_config.h"
-#include "chrome/browser/notifications/scheduler/scheduler_utils.h"
-
-using Notifications = notifications::DisplayDecider::Notifications;
-using Results = notifications::DisplayDecider::Results;
-using ClientStates = notifications::DisplayDecider::ClientStates;
-
-namespace notifications {
-namespace {
-
-// Helper class contains the actual logic to decide which notifications to show.
-// This is an one-shot class, callers should create a new object each time.
-class DecisionHelper {
- public:
-  DecisionHelper(const SchedulerConfig* config,
-                 const std::vector<SchedulerClientType>& clients,
-                 std::unique_ptr<DistributionPolicy> distribution_policy,
-                 SchedulerTaskTime task_start_time,
-                 Notifications notifications,
-                 ClientStates client_states)
-      : notifications_(std::move(notifications)),
-        current_task_start_time_(task_start_time),
-        client_states_(std::move(client_states)),
-        config_(config),
-        clients_(clients),
-        policy_(std::move(distribution_policy)),
-        daily_max_to_show_all_types_(0),
-        last_shown_type_(SchedulerClientType::kUnknown),
-        shown_(0) {}
-
-  ~DecisionHelper() = default;
-
-  // Figures out a list of notifications to show.
-  void DecideNotificationToShow(Results* results) {
-    ComputeDailyQuotaAllTypes();
-    CountNotificationsShownToday();
-    PickNotificationToShow(results);
-  }
-
- private:
-  void ComputeDailyQuotaAllTypes() {
-    // Only background task launch needs to comply to |policy_|.
-    if (current_task_start_time_ == SchedulerTaskTime::kUnknown) {
-      daily_max_to_show_all_types_ = config_->max_daily_shown_all_type;
-      return;
-    }
-
-    int quota = std::max(config_->max_daily_shown_all_type - shown_, 0);
-    DCHECK(policy_);
-    daily_max_to_show_all_types_ =
-        std::min(config_->max_daily_shown_all_type,
-                 policy_->MaxToShow(current_task_start_time_, quota));
-  }
-
-  void CountNotificationsShownToday() {
-    for (const auto& pair : client_states_) {
-      auto type = pair.first;
-      // TODO(xingliu): Ensure deprecated clients will not have data in storage.
-      DCHECK(std::find(clients_.begin(), clients_.end(), type) !=
-             clients_.end());
-    }
-
-    NotificationsShownToday(client_states_, &shown_per_type_, &shown_,
-                            &last_shown_type_);
-  }
-
-  // Picks a list of notifications to show.
-  void PickNotificationToShow(Results* to_show) {
-    DCHECK(to_show);
-    if (shown_ > config_->max_daily_shown_all_type || clients_.empty())
-      return;
-
-    // No previous shown notification, move the iterator to last element.
-    // We will iterate through all client types later.
-    auto it = std::find(clients_.begin(), clients_.end(), last_shown_type_);
-    if (it == clients_.end()) {
-      DCHECK_EQ(last_shown_type_, SchedulerClientType::kUnknown);
-      last_shown_type_ = clients_.back();
-      it = clients_.end() - 1;
-    }
-
-    DCHECK_NE(last_shown_type_, SchedulerClientType::kUnknown);
-    size_t steps = 0u;
-
-    // Circling around all clients to find new notification to show.
-    // TODO(xingliu): Apply scheduling parameters here.
-    do {
-      // Move the iterator to next client type.
-      DCHECK(it != clients_.end());
-      if (++it == clients_.end())
-        it = clients_.begin();
-      ++steps;
-
-      SchedulerClientType type = *it;
-
-      // Check quota for all types and current background task type.
-      if (ReachDailyQuota())
-        break;
-
-      // Check quota for this type, and continue to iterate other types.
-      if (NoMoreNotificationToShow(type))
-        continue;
-
-      // Show the last notification in the vector. Notice the order depends on
-      // how the vector is sorted.
-      to_show->emplace(notifications_[type].back()->guid);
-      notifications_[type].pop_back();
-      shown_per_type_[type]++;
-      shown_++;
-      steps = 0u;
-
-      // Stop if we didn't find anything new to show, and have looped around
-      // all clients.
-    } while (steps <= clients_.size());
-  }
-
-  bool NoMoreNotificationToShow(SchedulerClientType type) {
-    auto it = client_states_.find(type);
-    int max_daily_show =
-        it == client_states_.end() ? 0 : it->second->current_max_daily_show;
-
-    return notifications_[type].empty() ||
-           shown_per_type_[type] >= config_->max_daily_shown_per_type ||
-           shown_per_type_[type] >= max_daily_show;
-  }
-
-  bool ReachDailyQuota() const {
-    return shown_ >= daily_max_to_show_all_types_;
-  }
-
-  // Scheduled notifications as candidates to display to the user.
-  Notifications notifications_;
-
-  const SchedulerTaskTime current_task_start_time_;
-  const ClientStates client_states_;
-  const SchedulerConfig* config_;
-  const std::vector<SchedulerClientType> clients_;
-  std::unique_ptr<DistributionPolicy> policy_;
-  int daily_max_to_show_all_types_;
-
-  SchedulerClientType last_shown_type_;
-  std::map<SchedulerClientType, int> shown_per_type_;
-  int shown_;
-
-  DISALLOW_COPY_AND_ASSIGN(DecisionHelper);
-};
-
-class DisplayDeciderImpl : public DisplayDecider {
- public:
-  DisplayDeciderImpl() = default;
-  ~DisplayDeciderImpl() override = default;
-
- private:
-  // DisplayDecider implementation.
-  void FindNotificationsToShow(
-      const SchedulerConfig* config,
-      std::vector<SchedulerClientType> clients,
-      std::unique_ptr<DistributionPolicy> distribution_policy,
-      SchedulerTaskTime task_start_time,
-      Notifications notifications,
-      ClientStates client_states,
-      Results* results) override {
-    auto helper = std::make_unique<DecisionHelper>(
-        config, std::move(clients), std::move(distribution_policy),
-        task_start_time, std::move(notifications), std::move(client_states));
-    helper->DecideNotificationToShow(results);
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(DisplayDeciderImpl);
-};
-
-}  // namespace
-
-// static
-std::unique_ptr<DisplayDecider> DisplayDecider::Create() {
-  return std::make_unique<DisplayDeciderImpl>();
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/display_decider.h b/chrome/browser/notifications/scheduler/display_decider.h
deleted file mode 100644
index 2e4187c6..0000000
--- a/chrome/browser/notifications/scheduler/display_decider.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_DISPLAY_DECIDER_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_DISPLAY_DECIDER_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <utility>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace notifications {
-
-class DistributionPolicy;
-struct ClientState;
-struct NotificationEntry;
-struct SchedulerConfig;
-
-// This class uses scheduled notifications data and notification impression data
-// of each notification type to find a list of notification that should be
-// displayed to the user.
-// All operations should be done on the main thread.
-class DisplayDecider {
- public:
-  using Notifications =
-      std::map<SchedulerClientType, std::vector<const NotificationEntry*>>;
-  using ClientStates = std::map<SchedulerClientType, const ClientState*>;
-  using Results = std::set<std::string>;
-
-  // Creates the decider to determine notifications to show.
-  static std::unique_ptr<DisplayDecider> Create();
-
-  DisplayDecider() = default;
-  virtual ~DisplayDecider() = default;
-
-  // Finds notifications to show. Returns a list of notification guids.
-  virtual void FindNotificationsToShow(
-      const SchedulerConfig* config,
-      std::vector<SchedulerClientType> clients,
-      std::unique_ptr<DistributionPolicy> distribution_policy,
-      SchedulerTaskTime task_start_time,
-      Notifications notifications,
-      ClientStates client_states,
-      Results* results) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DisplayDecider);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_DISPLAY_DECIDER_H_
diff --git a/chrome/browser/notifications/scheduler/display_decider_unittest.cc b/chrome/browser/notifications/scheduler/display_decider_unittest.cc
deleted file mode 100644
index 8d606339..0000000
--- a/chrome/browser/notifications/scheduler/display_decider_unittest.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/display_decider.h"
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "base/strings/stringprintf.h"
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/notifications/scheduler/distribution_policy.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-#include "chrome/browser/notifications/scheduler/scheduler_config.h"
-#include "chrome/browser/notifications/scheduler/test/test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace notifications {
-namespace {
-
-// Default suppression info used in this test.
-const SuppressionInfo kSuppressionInfo =
-    SuppressionInfo(base::Time::Now(), base::TimeDelta::FromDays(56));
-
-// Initial state for test cases with a single registered client.
-const std::vector<test::ImpressionTestData> kSingleClientImpressionTestData = {
-    {SchedulerClientType::kTest1,
-     2 /* current_max_daily_show */,
-     {},
-     base::nullopt /* suppression_info */}};
-
-const std::vector<test::ImpressionTestData> kClientsImpressionTestData = {
-    {SchedulerClientType::kTest1,
-     2 /* current_max_daily_show */,
-     {},
-     base::nullopt /* suppression_info */},
-    {SchedulerClientType::kTest2,
-     5 /* current_max_daily_show */,
-     {},
-     base::nullopt /* suppression_info */},
-    {SchedulerClientType::kTest3,
-     0 /* current_max_daily_show */,
-     {},
-     kSuppressionInfo}};
-
-struct TestData {
-  // Impression data as the input.
-  std::vector<test::ImpressionTestData> impression_test_data;
-
-  // Notification entries as the input.
-  std::vector<NotificationEntry> notification_entries;
-
-  // The type of current background task.
-  SchedulerTaskTime task_start_time = SchedulerTaskTime::kUnknown;
-
-  // Expected output data.
-  DisplayDecider::Results expected;
-};
-
-std::string DebugString(const DisplayDecider::Results& results) {
-  std::string debug_string("notifications_to_show: \n");
-  for (const auto& guid : results)
-    debug_string += base::StringPrintf("%s ", guid.c_str());
-
-  return debug_string;
-}
-
-// TODO(xingliu): Add more test cases.
-class DisplayDeciderTest : public testing::Test {
- public:
-  DisplayDeciderTest() = default;
-  ~DisplayDeciderTest() override = default;
-
-  void SetUp() override {
-    // Setup configuration used by this test.
-    config_.morning_task_hour = 7;
-    config_.evening_task_hour = 18;
-    config_.max_daily_shown_all_type = 3;
-  }
-
- protected:
-  // Initializes a test case with input data.
-  void RunTestCase(const TestData& test_data) {
-    test_data_ = test_data;
-    test::AddImpressionTestData(test_data_.impression_test_data,
-                                &client_states_);
-
-    DisplayDecider::Notifications notifications;
-    for (const auto& entry : test_data_.notification_entries) {
-      notifications[entry.type].emplace_back(&entry);
-    }
-    std::vector<SchedulerClientType> clients;
-
-    std::map<SchedulerClientType, const ClientState*> client_states;
-    for (const auto& type : client_states_) {
-      client_states.emplace(type.first, type.second.get());
-      clients.emplace_back(type.first);
-    }
-
-    // Copy test inputs into |decider_|.
-    decider_ = DisplayDecider::Create();
-    decider_->FindNotificationsToShow(
-        &config_, std::move(clients), DistributionPolicy::Create(),
-        test_data_.task_start_time, std::move(notifications),
-        std::move(client_states), &results_);
-
-    // Verify output.
-    EXPECT_EQ(results_, test_data_.expected)
-        << "Actual result: \n"
-        << DebugString(results_) << " \n Expected result: \n"
-        << DebugString(test_data_.expected);
-  }
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  TestData test_data_;
-  SchedulerConfig config_;
-
-  std::map<SchedulerClientType, std::unique_ptr<ClientState>> client_states_;
-
-  // Test target class and output.
-  std::unique_ptr<DisplayDecider> decider_;
-  DisplayDecider::Results results_;
-
-  DISALLOW_COPY_AND_ASSIGN(DisplayDeciderTest);
-};
-
-TEST_F(DisplayDeciderTest, NoNotification) {
-  TestData data{kClientsImpressionTestData,
-                {},
-                SchedulerTaskTime::kEvening,
-                DisplayDecider::Results()};
-  RunTestCase(data);
-}
-
-// Simple test case to verify new notifcaiton can be selected to show.
-TEST_F(DisplayDeciderTest, PickNewMorning) {
-  NotificationEntry entry(SchedulerClientType::kTest1, "guid123");
-  DisplayDecider::Results expected = {"guid123"};
-  TestData data{kSingleClientImpressionTestData,
-                {entry},
-                SchedulerTaskTime::kMorning,
-                std::move(expected)};
-  RunTestCase(data);
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/distribution_policy.cc b/chrome/browser/notifications/scheduler/distribution_policy.cc
deleted file mode 100644
index b30cbfc..0000000
--- a/chrome/browser/notifications/scheduler/distribution_policy.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/distribution_policy.h"
-
-#include "base/logging.h"
-
-namespace notifications {
-namespace {
-
-// Evenly distributes notifications to show in morning and evening. Morning
-// will have one more to show if the total quota is odd.
-class EvenDistributionHigherMorning : public DistributionPolicy {
- public:
-  EvenDistributionHigherMorning() = default;
-  ~EvenDistributionHigherMorning() override = default;
-
- private:
-  // DistributionPolicy implementation.
-  int MaxToShow(SchedulerTaskTime task_start_time, int quota) override {
-    DCHECK_GE(quota, 0);
-    switch (task_start_time) {
-      case SchedulerTaskTime::kUnknown:
-        NOTREACHED();
-        return quota;
-      case SchedulerTaskTime::kMorning:
-        return quota / 2 + quota % 2;
-      case SchedulerTaskTime::kEvening:
-        // The task running in the evening should flush all the remaining
-        // notifications.
-        return quota;
-    }
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(EvenDistributionHigherMorning);
-};
-
-}  // namespace
-
-// static
-std::unique_ptr<DistributionPolicy> DistributionPolicy::Create() {
-  return std::make_unique<EvenDistributionHigherMorning>();
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/distribution_policy.h b/chrome/browser/notifications/scheduler/distribution_policy.h
deleted file mode 100644
index 762ba338..0000000
--- a/chrome/browser/notifications/scheduler/distribution_policy.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_DISTRIBUTION_POLICY_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_DISTRIBUTION_POLICY_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace notifications {
-
-// Defines how to distribute notifications to show in different background tasks
-// in a day.
-class DistributionPolicy {
- public:
-  // Creates the default distribution policy.
-  static std::unique_ptr<DistributionPolicy> Create();
-
-  DistributionPolicy() = default;
-  virtual ~DistributionPolicy() = default;
-
-  // Returns the maximum number of notifications to show in the background task
-  // starts at |task_start_time|. Suppose we at most can show |quota| number of
-  // new notifications during the current background task.
-  virtual int MaxToShow(SchedulerTaskTime task_start_time, int quota) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DistributionPolicy);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_DISTRIBUTION_POLICY_H_
diff --git a/chrome/browser/notifications/scheduler/distribution_policy_unittest.cc b/chrome/browser/notifications/scheduler/distribution_policy_unittest.cc
deleted file mode 100644
index 320b900..0000000
--- a/chrome/browser/notifications/scheduler/distribution_policy_unittest.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/distribution_policy.h"
-
-#include "base/logging.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace notifications {
-namespace {
-
-int MaxToShow(DistributionPolicy* policy,
-              SchedulerTaskTime task_start_time,
-              int quota) {
-  DCHECK(policy);
-  return policy->MaxToShow(task_start_time, quota);
-}
-
-TEST(DistributionPolicyTest, EvenDistributionHigherMorning) {
-  auto policy = DistributionPolicy::Create();
-
-  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 5 /* quota */),
-            3);
-  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 4 /* quota */),
-            2);
-
-  // Evening task should flush all remaining quota.
-  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kEvening, 5 /* quota */),
-            5);
-  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kEvening, 4 /* quota */),
-            4);
-
-  // Test 0 quota.
-  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 0 /* quota */),
-            0);
-  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 0 /* quota */),
-            0);
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/icon_entry.cc b/chrome/browser/notifications/scheduler/icon_entry.cc
deleted file mode 100644
index 01f87e0..0000000
--- a/chrome/browser/notifications/scheduler/icon_entry.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/icon_entry.h"
-
-namespace notifications {
-
-IconEntry::IconEntry() = default;
-
-IconEntry::IconEntry(IconEntry&& other) {
-  uuid.swap(other.uuid);
-  data.swap(other.data);
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/icon_entry.h b/chrome/browser/notifications/scheduler/icon_entry.h
deleted file mode 100644
index 039c295..0000000
--- a/chrome/browser/notifications/scheduler/icon_entry.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_ICON_ENTRY_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_ICON_ENTRY_H_
-
-#include <string>
-#include <utility>
-
-#include "base/macros.h"
-
-namespace notifications {
-
-// The database entry that contains a notification icon, deserialized from the
-// icon protobuffer.
-// The icon can be a large chunk of memory so should be used in caution. The
-// format of the data is the same as the format in the protobuffer, and may need
-// to be converted to bitmap when used by the UI.
-struct IconEntry {
-  using IconData = std::string;
-
-  IconEntry();
-  IconEntry(IconEntry&& other);
-
-  // Unique identifier for the icon database entry.
-  std::string uuid;
-
-  // Raw data of the icon.
-  IconData data;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(IconEntry);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_ICON_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/icon_store.cc b/chrome/browser/notifications/scheduler/icon_store.cc
deleted file mode 100644
index a228d38..0000000
--- a/chrome/browser/notifications/scheduler/icon_store.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/icon_store.h"
-
-#include <utility>
-
-#include "chrome/browser/notifications/scheduler/icon_entry.h"
-#include "chrome/browser/notifications/scheduler/proto_conversion.h"
-
-namespace leveldb_proto {
-
-void DataToProto(notifications::IconEntry* icon_entry,
-                 notifications::proto::Icon* proto) {
-  IconEntryToProto(icon_entry, proto);
-}
-
-void ProtoToData(notifications::proto::Icon* proto,
-                 notifications::IconEntry* icon_entry) {
-  IconEntryFromProto(proto, icon_entry);
-}
-
-}  // namespace leveldb_proto
-
-namespace notifications {
-
-using KeyEntryPair = std::pair<std::string, IconEntry>;
-using KeyEntryVector = std::vector<KeyEntryPair>;
-using KeyVector = std::vector<std::string>;
-
-IconProtoDbStore::IconProtoDbStore(
-    std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db)
-    : db_(std::move(db)), weak_ptr_factory_(this) {}
-
-IconProtoDbStore::~IconProtoDbStore() = default;
-
-void IconProtoDbStore::Init(InitCallback callback) {
-  db_->Init(base::BindOnce(&IconProtoDbStore::OnDbInitialized,
-                           weak_ptr_factory_.GetWeakPtr(),
-                           std::move(callback)));
-}
-
-void IconProtoDbStore::Load(const std::string& key, LoadCallback callback) {
-  db_->GetEntry(
-      key, base::BindOnce(&IconProtoDbStore::OnIconEntryLoaded,
-                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void IconProtoDbStore::Add(const std::string& key,
-                           std::unique_ptr<IconEntry> entry,
-                           UpdateCallback callback) {
-  auto entries_to_save = std::make_unique<KeyEntryVector>();
-  // TODO(xingliu): See if proto database can use
-  // std::vector<std::unique_ptr<T>> to avoid copy T into std::pair. Currently
-  // some code uses the copy constructor that force T to be copyable.
-  entries_to_save->emplace_back(
-      std::make_pair(key, std::move(*entry.release())));
-  db_->UpdateEntries(std::move(entries_to_save), std::make_unique<KeyVector>(),
-                     std::move(callback));
-}
-
-void IconProtoDbStore::Delete(const std::string& key, UpdateCallback callback) {
-  auto keys_to_delete = std::make_unique<KeyVector>();
-  keys_to_delete->emplace_back(key);
-  db_->UpdateEntries(std::make_unique<KeyEntryVector>(),
-                     std::move(keys_to_delete), std::move(callback));
-}
-
-void IconProtoDbStore::OnDbInitialized(
-    InitCallback callback,
-    leveldb_proto::Enums::InitStatus status) {
-  bool success = (status == leveldb_proto::Enums::InitStatus::kOK);
-  std::move(callback).Run(success);
-}
-
-void IconProtoDbStore::OnIconEntryLoaded(
-    LoadCallback callback,
-    bool success,
-    std::unique_ptr<IconEntry> icon_entry) {
-  if (!success) {
-    std::move(callback).Run(false, nullptr);
-    return;
-  }
-
-  std::move(callback).Run(true, std::move(icon_entry));
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/icon_store.h b/chrome/browser/notifications/scheduler/icon_store.h
deleted file mode 100644
index 2fece4e..0000000
--- a/chrome/browser/notifications/scheduler/icon_store.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_ICON_STORE_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_ICON_STORE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/notifications/proto/icon.pb.h"
-#include "chrome/browser/notifications/scheduler/icon_entry.h"
-#include "components/leveldb_proto/public/proto_database.h"
-
-// Forward declaration for proto conversion.
-namespace leveldb_proto {
-void DataToProto(notifications::IconEntry* icon_entry,
-                 notifications::proto::Icon* proto);
-
-void ProtoToData(notifications::proto::Icon* proto,
-                 notifications::IconEntry* icon_entry);
-}  // namespace leveldb_proto
-
-namespace notifications {
-
-// Storage interface used to read/write icon data, each time only one icon can
-// be loaded into memory.
-class IconStore {
- public:
-  using LoadCallback =
-      base::OnceCallback<void(bool, std::unique_ptr<IconEntry>)>;
-  using InitCallback = base::OnceCallback<void(bool)>;
-  using UpdateCallback = base::OnceCallback<void(bool)>;
-
-  // Initializes the storage.
-  virtual void Init(InitCallback callback) = 0;
-
-  // Loads one icon.
-  virtual void Load(const std::string& key, LoadCallback callback) = 0;
-
-  // Adds one icon to storage.
-  virtual void Add(const std::string& key,
-                   std::unique_ptr<IconEntry> entry,
-                   UpdateCallback callback) = 0;
-
-  // Deletes an icon.
-  virtual void Delete(const std::string& key, UpdateCallback callback) = 0;
-
-  IconStore() = default;
-  virtual ~IconStore() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(IconStore);
-};
-
-// IconStore implementation backed by a proto database.
-class IconProtoDbStore : public IconStore {
- public:
-  explicit IconProtoDbStore(
-      std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db);
-  ~IconProtoDbStore() override;
-
- private:
-  // IconStore implementation.
-  void Init(InitCallback callback) override;
-  void Load(const std::string& key, LoadCallback callback) override;
-  void Add(const std::string& key,
-           std::unique_ptr<IconEntry> entry,
-           UpdateCallback callback) override;
-  void Delete(const std::string& key, UpdateCallback callback) override;
-
-  // Called when the proto database is initialized.
-  void OnDbInitialized(InitCallback callback,
-                       leveldb_proto::Enums::InitStatus status);
-
-  // Called when the icon is retrieved from the database.
-  void OnIconEntryLoaded(LoadCallback callback,
-                         bool success,
-                         std::unique_ptr<IconEntry> icon_entry);
-
-  // The proto database instance that persists data.
-  std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db_;
-
-  base::WeakPtrFactory<IconProtoDbStore> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(IconProtoDbStore);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_ICON_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/icon_store_unittest.cc b/chrome/browser/notifications/scheduler/icon_store_unittest.cc
deleted file mode 100644
index c15c4c8..0000000
--- a/chrome/browser/notifications/scheduler/icon_store_unittest.cc
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/icon_store.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/notifications/proto/icon.pb.h"
-#include "chrome/browser/notifications/scheduler/icon_entry.h"
-#include "components/leveldb_proto/testing/fake_db.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace notifications {
-namespace {
-
-const char kEntryKey[] = "guid_key1";
-const char kEntryKey2[] = "guid_key2";
-const char kEntryId[] = "proto_id_1";
-const char kEntryId2[] = "proto_id_2";
-const char kEntryData[] = "data_1";
-const char kEntryData2[] = "data_2";
-
-class IconStoreTest : public testing::Test {
- public:
-  IconStoreTest() : load_result_(false), db_(nullptr) {}
-  ~IconStoreTest() override = default;
-
-  void SetUp() override {
-    IconEntry entry;
-    entry.uuid = kEntryId;
-    entry.data = kEntryData;
-    proto::Icon proto;
-    leveldb_proto::DataToProto(&entry, &proto);
-    db_entries_.emplace(kEntryKey, proto);
-
-    auto db =
-        std::make_unique<leveldb_proto::test::FakeDB<proto::Icon, IconEntry>>(
-            &db_entries_);
-    db_ = db.get();
-    store_ = std::make_unique<IconProtoDbStore>(std::move(db));
-  }
-
-  void InitDb() {
-    store()->Init(base::DoNothing());
-    db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
-  }
-
-  void Load(const std::string& key) {
-    store()->Load(key, base::BindOnce(&IconStoreTest::OnEntryLoaded,
-                                      base::Unretained(this)));
-  }
-
-  void OnEntryLoaded(bool success, std::unique_ptr<IconEntry> entry) {
-    loaded_entry_ = std::move(entry);
-    load_result_ = success;
-  }
-
- protected:
-  IconStore* store() { return store_.get(); }
-  leveldb_proto::test::FakeDB<proto::Icon, IconEntry>* db() { return db_; }
-  bool load_result() const { return load_result_; }
-  IconEntry* loaded_entry() { return loaded_entry_.get(); }
-
-  void VerifyEntry(const std::string& uuid, const std::string& data) {
-    DCHECK(loaded_entry_);
-    EXPECT_EQ(loaded_entry_->uuid, uuid);
-    EXPECT_EQ(loaded_entry_->data, data);
-  }
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  std::unique_ptr<IconStore> store_;
-  std::map<std::string, proto::Icon> db_entries_;
-  std::unique_ptr<IconEntry> loaded_entry_;
-  bool load_result_;
-  leveldb_proto::test::FakeDB<proto::Icon, IconEntry>* db_;
-
-  DISALLOW_COPY_AND_ASSIGN(IconStoreTest);
-};
-
-TEST_F(IconStoreTest, Init) {
-  store()->Init(base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
-  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(IconStoreTest, InitFailed) {
-  store()->Init(base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
-  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kCorrupt);
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(IconStoreTest, LoadOne) {
-  InitDb();
-  Load(kEntryKey);
-  db()->GetCallback(true);
-
-  // Verify data is loaded.
-  DCHECK(loaded_entry());
-  EXPECT_TRUE(load_result());
-  VerifyEntry(kEntryId, kEntryData);
-}
-
-TEST_F(IconStoreTest, LoadFailed) {
-  InitDb();
-  Load(kEntryKey);
-  db()->GetCallback(false);
-
-  // Verify load failure.
-  EXPECT_FALSE(loaded_entry());
-  EXPECT_FALSE(load_result());
-}
-
-TEST_F(IconStoreTest, Add) {
-  InitDb();
-
-  auto new_entry = std::make_unique<IconEntry>();
-  new_entry->uuid = kEntryId2;
-  new_entry->data = kEntryData;
-
-  store()->Add(kEntryKey2, std::move(new_entry), base::DoNothing());
-  db()->UpdateCallback(true);
-
-  // Verify the entry is added.
-  Load(kEntryKey2);
-  db()->GetCallback(true);
-  EXPECT_TRUE(load_result());
-  VerifyEntry(kEntryId2, kEntryData);
-}
-
-TEST_F(IconStoreTest, AddDuplicate) {
-  InitDb();
-
-  auto new_entry = std::make_unique<IconEntry>();
-  new_entry->uuid = kEntryId;
-  new_entry->data = kEntryData2;
-
-  store()->Add(kEntryKey, std::move(new_entry), base::DoNothing());
-  db()->UpdateCallback(true);
-
-  // Add a duplicate id is currently allowed, we just update the entry.
-  Load(kEntryKey);
-  db()->GetCallback(true);
-  EXPECT_TRUE(load_result());
-  VerifyEntry(kEntryId, kEntryData2);
-}
-
-TEST_F(IconStoreTest, Delete) {
-  InitDb();
-
-  // Delete the only entry.
-  store()->Delete(kEntryKey,
-                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
-  db()->UpdateCallback(true);
-
-  // No entry can be loaded, move nullptr as result.
-  Load(kEntryKey);
-  db()->GetCallback(true);
-  EXPECT_FALSE(loaded_entry());
-  EXPECT_TRUE(load_result());
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/impression_history_tracker.cc
deleted file mode 100644
index def4dc4..0000000
--- a/chrome/browser/notifications/scheduler/impression_history_tracker.cc
+++ /dev/null
@@ -1,392 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/impression_history_tracker.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/numerics/ranges.h"
-
-namespace notifications {
-
-// Comparator used to sort notification entries based on creation time.
-bool CreateTimeCompare(const Impression& lhs, const Impression& rhs) {
-  return lhs.create_time < rhs.create_time;
-}
-
-std::string ToDatabaseKey(SchedulerClientType type) {
-  switch (type) {
-    case SchedulerClientType::kTest1:
-      return "Test1";
-    case SchedulerClientType::kTest2:
-      return "Test2";
-    case SchedulerClientType::kTest3:
-      return "Test3";
-    case SchedulerClientType::kUnknown:
-      NOTREACHED();
-      return std::string();
-  }
-}
-
-ImpressionHistoryTrackerImpl::ImpressionHistoryTrackerImpl(
-    const SchedulerConfig& config,
-    std::unique_ptr<CollectionStore<ClientState>> store)
-    : store_(std::move(store)),
-      config_(config),
-      initialized_(false),
-      delegate_(nullptr),
-      weak_ptr_factory_(this) {}
-
-ImpressionHistoryTrackerImpl::~ImpressionHistoryTrackerImpl() = default;
-
-void ImpressionHistoryTrackerImpl::Init(Delegate* delegate,
-                                        InitCallback callback) {
-  DCHECK(!delegate_ && delegate);
-  delegate_ = delegate;
-  store_->InitAndLoad(
-      base::BindOnce(&ImpressionHistoryTrackerImpl::OnStoreInitialized,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory() {
-  for (auto& client_state : client_states_)
-    AnalyzeImpressionHistory(client_state.second.get());
-  if (MaybeUpdateAllDb())
-    NotifyImpressionUpdate();
-}
-
-void ImpressionHistoryTrackerImpl::GetClientStates(
-    std::map<SchedulerClientType, const ClientState*>* client_states) const {
-  DCHECK(client_states);
-  client_states->clear();
-  for (const auto& pair : client_states_) {
-    client_states->emplace(pair.first, pair.second.get());
-  }
-}
-
-void ImpressionHistoryTrackerImpl::OnClick(const std::string& notification_id) {
-  OnClickInternal(notification_id, true /*update_db*/);
-}
-
-void ImpressionHistoryTrackerImpl::OnActionClick(
-    const std::string& notification_id,
-    ActionButtonType button_type) {
-  OnButtonClickInternal(notification_id, button_type, true /*update_db*/);
-}
-
-void ImpressionHistoryTrackerImpl::OnDismiss(
-    const std::string& notification_id) {
-  OnDismissInternal(notification_id, true /*update_db*/);
-}
-
-void ImpressionHistoryTrackerImpl::OnStoreInitialized(
-    InitCallback callback,
-    bool success,
-    CollectionStore<ClientState>::Entries entries) {
-  if (!success) {
-    std::move(callback).Run(false);
-    return;
-  }
-
-  initialized_ = true;
-
-  // Load the data to memory, and sort the impression list.
-  // TODO(xingliu): Persist ClientState for new registered client and remove
-  // deprecated client. https://crbug.com/968606.
-  for (auto it = entries.begin(); it != entries.end(); ++it) {
-    auto& entry = (*it);
-    auto type = entry->type;
-    std::sort(entry->impressions.begin(), entry->impressions.end(),
-              &CreateTimeCompare);
-    for (auto& impression : entry->impressions) {
-      impression_map_.emplace(impression.guid, &impression);
-    }
-    client_states_.emplace(type, std::move(*it));
-  }
-
-  std::move(callback).Run(true);
-}
-
-void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory(
-    ClientState* client_state) {
-  DCHECK(client_state);
-  std::deque<Impression*> dismisses;
-  base::Time now = base::Time::Now();
-
-  for (auto it = client_state->impressions.begin();
-       it != client_state->impressions.end();) {
-    auto* impression = &*it;
-
-    // Prune out expired impression.
-    if (now - impression->create_time > config_.impression_expiration) {
-      impression_map_.erase(impression->guid);
-      client_state->impressions.erase(it++);
-      SetNeedsUpdate(client_state->type, true);
-      continue;
-    } else {
-      ++it;
-    }
-
-    // TODO(xingliu): Use scheduling params to determine ImpressionResult.
-    // https://crbug.com/968880
-    switch (impression->feedback) {
-      case UserFeedback::kDismiss:
-        dismisses.emplace_back(impression);
-        PruneImpressionByCreateTime(
-            &dismisses, impression->create_time - config_.dismiss_duration);
-
-        // Three consecutive dismisses will result in suppression.
-        ApplyNegativeImpressions(client_state, &dismisses,
-                                 config_.dismiss_count);
-        break;
-      case UserFeedback::kClick:
-        OnClickInternal(impression->guid, false /*update_db*/);
-        break;
-      case UserFeedback::kHelpful:
-        OnButtonClickInternal(impression->guid, ActionButtonType::kHelpful,
-                              false /*update_db*/);
-        break;
-      case UserFeedback::kNotHelpful:
-        OnButtonClickInternal(impression->guid, ActionButtonType::kUnhelpful,
-                              false /*update_db*/);
-        break;
-      case UserFeedback::kIgnore:
-        break;
-      case UserFeedback::kNoFeedback:
-        FALLTHROUGH;
-      default:
-        // The user didn't interact with the notification yet.
-        continue;
-        break;
-    }
-  }
-
-  // Check suppression expiration.
-  CheckSuppressionExpiration(client_state);
-}
-
-// static
-void ImpressionHistoryTrackerImpl::PruneImpressionByCreateTime(
-    std::deque<Impression*>* impressions,
-    const base::Time& start_time) {
-  DCHECK(impressions);
-  while (!impressions->empty()) {
-    if (impressions->front()->create_time > start_time)
-      break;
-    // Anything created before |start_time| is considered to have no effect and
-    // will never be processed again.
-    impressions->front()->integrated = true;
-    impressions->pop_front();
-  }
-}
-
-void ImpressionHistoryTrackerImpl::ApplyPositiveImpression(
-    ClientState* client_state,
-    Impression* impression) {
-  DCHECK(impression);
-  if (impression->integrated)
-    return;
-
-  SetNeedsUpdate(client_state->type, true);
-  impression->integrated = true;
-  impression->impression = ImpressionResult::kPositive;
-
-  // A positive impression directly releases the suppression.
-  if (client_state->suppression_info.has_value()) {
-    client_state->current_max_daily_show =
-        client_state->suppression_info->recover_goal;
-    client_state->suppression_info.reset();
-    return;
-  }
-
-  // Increase |current_max_daily_show| by 1.
-  client_state->current_max_daily_show =
-      base::ClampToRange(++client_state->current_max_daily_show, 0,
-                         config_.max_daily_shown_per_type);
-}
-
-void ImpressionHistoryTrackerImpl::ApplyNegativeImpressions(
-    ClientState* client_state,
-    std::deque<Impression*>* impressions,
-    size_t num_actions) {
-  if (impressions->size() < num_actions)
-    return;
-
-  // Suppress the notification if the user performed consecutive operations that
-  // generates negative impressions.
-  for (size_t i = 0, size = impressions->size(); i < size; ++i) {
-    if ((*impressions)[i]->integrated)
-      continue;
-
-    (*impressions)[i]->integrated = true;
-
-    // Each user feedback after |num_action| will apply a new negative
-    // impression.
-    if (i + 1 >= num_actions)
-      ApplyNegativeImpression(client_state, (*impressions)[i]);
-  }
-}
-
-void ImpressionHistoryTrackerImpl::ApplyNegativeImpression(
-    ClientState* client_state,
-    Impression* impression) {
-  if (impression->integrated)
-    return;
-
-  SetNeedsUpdate(client_state->type, true);
-  impression->integrated = true;
-  impression->impression = ImpressionResult::kNegative;
-
-  // Suppress the notification, the user will not see this type of notification
-  // for a while.
-  SuppressionInfo supression_info(base::Time::Now(),
-                                  config_.suppression_duration);
-  client_state->suppression_info = std::move(supression_info);
-  client_state->current_max_daily_show = 0;
-}
-
-void ImpressionHistoryTrackerImpl::CheckSuppressionExpiration(
-    ClientState* client_state) {
-  // No suppression to recover from.
-  if (!client_state->suppression_info.has_value())
-    return;
-
-  SuppressionInfo& suppression = client_state->suppression_info.value();
-  base::Time now = base::Time::Now();
-
-  // Still in the suppression time window.
-  if (now - suppression.last_trigger_time < suppression.duration)
-    return;
-
-  // Recover from suppression and increase |current_max_daily_show|.
-  DCHECK_EQ(client_state->current_max_daily_show, 0);
-  client_state->current_max_daily_show = suppression.recover_goal;
-
-  // Clear suppression if fully recovered.
-  client_state->suppression_info.reset();
-  SetNeedsUpdate(client_state->type, true);
-}
-
-bool ImpressionHistoryTrackerImpl::MaybeUpdateDb(SchedulerClientType type) {
-  auto it = client_states_.find(type);
-  if (it == client_states_.end())
-    return false;
-
-  bool db_updated = false;
-  if (NeedsUpdate(type)) {
-    store_->Update(ToDatabaseKey(type), *(it->second.get()), base::DoNothing());
-    db_updated = true;
-  }
-  SetNeedsUpdate(type, false);
-  return db_updated;
-}
-
-bool ImpressionHistoryTrackerImpl::MaybeUpdateAllDb() {
-  bool db_updated = false;
-  for (const auto& client_state : client_states_) {
-    auto type = client_state.second->type;
-    db_updated |= MaybeUpdateDb(type);
-  }
-
-  return db_updated;
-}
-
-void ImpressionHistoryTrackerImpl::SetNeedsUpdate(SchedulerClientType type,
-                                                  bool needs_update) {
-  need_update_db_[type] = needs_update;
-}
-
-bool ImpressionHistoryTrackerImpl::NeedsUpdate(SchedulerClientType type) const {
-  auto it = need_update_db_.find(type);
-  return it == need_update_db_.end() ? false : it->second;
-}
-
-void ImpressionHistoryTrackerImpl::NotifyImpressionUpdate() {
-  if (delegate_)
-    delegate_->OnImpressionUpdated();
-}
-
-Impression* ImpressionHistoryTrackerImpl::FindImpressionNeedsUpdate(
-    const std::string& notification_guid) {
-  auto it = impression_map_.find(notification_guid);
-  if (it == impression_map_.end())
-    return nullptr;
-  auto* impression = it->second;
-
-  if (impression->integrated)
-    return nullptr;
-
-  return it->second;
-}
-
-void ImpressionHistoryTrackerImpl::OnClickInternal(
-    const std::string& notification_guid,
-    bool update_db) {
-  auto* impression = FindImpressionNeedsUpdate(notification_guid);
-  if (!impression)
-    return;
-
-  auto it = client_states_.find(impression->type);
-  if (it == client_states_.end())
-    return;
-  ClientState* client_state = it->second.get();
-  ApplyPositiveImpression(client_state, impression);
-  impression->feedback = UserFeedback::kClick;
-
-  if (update_db && MaybeUpdateDb(client_state->type))
-    NotifyImpressionUpdate();
-}
-
-void ImpressionHistoryTrackerImpl::OnButtonClickInternal(
-    const std::string& notification_guid,
-    ActionButtonType button_type,
-    bool update_db) {
-  auto* impression = FindImpressionNeedsUpdate(notification_guid);
-  if (!impression)
-    return;
-  auto it = client_states_.find(impression->type);
-  if (it == client_states_.end())
-    return;
-
-  ClientState* client_state = it->second.get();
-  switch (button_type) {
-    case ActionButtonType::kHelpful:
-      ApplyPositiveImpression(client_state, impression);
-      impression->feedback = UserFeedback::kHelpful;
-      break;
-    case ActionButtonType::kUnhelpful:
-      ApplyNegativeImpression(client_state, impression);
-      impression->feedback = UserFeedback::kNotHelpful;
-      break;
-    case ActionButtonType::kUnknownAction:
-      NOTIMPLEMENTED();
-      break;
-  }
-
-  if (update_db && MaybeUpdateDb(client_state->type))
-    NotifyImpressionUpdate();
-}
-
-void ImpressionHistoryTrackerImpl::OnDismissInternal(
-    const std::string& notification_guid,
-    bool update_db) {
-  auto* impression = FindImpressionNeedsUpdate(notification_guid);
-  if (!impression)
-    return;
-
-  auto it = client_states_.find(impression->type);
-  if (it == client_states_.end())
-    return;
-  ClientState* client_state = it->second.get();
-
-  AnalyzeImpressionHistory(client_state);
-
-  if (update_db && MaybeUpdateDb(client_state->type))
-    NotifyImpressionUpdate();
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker.h b/chrome/browser/notifications/scheduler/impression_history_tracker.h
deleted file mode 100644
index e409fce..0000000
--- a/chrome/browser/notifications/scheduler/impression_history_tracker.h
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_IMPRESSION_HISTORY_TRACKER_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_HISTORY_TRACKER_H_
-
-#include <deque>
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/collection_store.h"
-#include "chrome/browser/notifications/scheduler/impression_types.h"
-#include "chrome/browser/notifications/scheduler/scheduler_config.h"
-#include "chrome/browser/notifications/scheduler/user_action_handler.h"
-
-namespace notifications {
-
-// Provides functionalities to update notification impression history and adjust
-// maximum daily notification shown to the user.
-class ImpressionHistoryTracker : public UserActionHandler {
- public:
-  using ClientStates =
-      std::map<SchedulerClientType, std::unique_ptr<ClientState>>;
-  using InitCallback = base::OnceCallback<void(bool)>;
-
-  class Delegate {
-   public:
-    Delegate() = default;
-    virtual ~Delegate() = default;
-
-    // Called when the impression data is updated.
-    virtual void OnImpressionUpdated() = 0;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Delegate);
-  };
-
-  // Initializes the impression tracker.
-  virtual void Init(Delegate* delegate, InitCallback callback) = 0;
-
-  // Analyzes the impression history for all notification clients, and adjusts
-  // the |current_max_daily_show|.
-  virtual void AnalyzeImpressionHistory() = 0;
-
-  // Queries the client states.
-  virtual void GetClientStates(
-      std::map<SchedulerClientType, const ClientState*>* client_states)
-      const = 0;
-
-  virtual ~ImpressionHistoryTracker() = default;
-
- protected:
-  ImpressionHistoryTracker() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTracker);
-};
-
-// An implementation of ImpressionHistoryTracker backed by a database.
-class ImpressionHistoryTrackerImpl : public ImpressionHistoryTracker {
- public:
-  explicit ImpressionHistoryTrackerImpl(
-      const SchedulerConfig& config,
-      std::unique_ptr<CollectionStore<ClientState>> store);
-  ~ImpressionHistoryTrackerImpl() override;
-
- private:
-  // ImpressionHistoryTracker implementation.
-  void Init(Delegate* delegate, InitCallback callback) override;
-  void AnalyzeImpressionHistory() override;
-  void GetClientStates(std::map<SchedulerClientType, const ClientState*>*
-                           client_states) const override;
-  void OnClick(const std::string& notification_id) override;
-  void OnActionClick(const std::string& notification_id,
-                     ActionButtonType button_type) override;
-  void OnDismiss(const std::string& notification_id) override;
-
-  // Called after |store_| is initialized.
-  void OnStoreInitialized(InitCallback callback,
-                          bool success,
-                          CollectionStore<ClientState>::Entries entries);
-
-  // Helper method to prune impressions created before |start_time|. Assumes
-  // |impressions| are sorted by creation time.
-  static void PruneImpressionByCreateTime(std::deque<Impression*>* impressions,
-                                          const base::Time& start_time);
-
-  // Analyzes the impression history for a particular client.
-  void AnalyzeImpressionHistory(ClientState* client_state);
-
-  // Applies a positive impression result to this notification type.
-  void ApplyPositiveImpression(ClientState* client_state,
-                               Impression* impression);
-
-  // Applies negative impression on this notification type when |num_actions|
-  // consecutive negative impression result are generated.
-  void ApplyNegativeImpressions(ClientState* client_state,
-                                std::deque<Impression*>* impressions,
-                                size_t num_actions);
-
-  // Applies one negative impression.
-  void ApplyNegativeImpression(ClientState* client_state,
-                               Impression* impression);
-
-  // Checks if suppression is expired and recover to a certain daily quota.
-  void CheckSuppressionExpiration(ClientState* client_state);
-
-  // Tries to update the database records for |type|. Returns whether the db is
-  // actually updated.
-  bool MaybeUpdateDb(SchedulerClientType type);
-  bool MaybeUpdateAllDb();
-
-  // Sets/Gets the flag if impression data for |type| needs update in the
-  // database.
-  void SetNeedsUpdate(SchedulerClientType type, bool needs_update);
-  bool NeedsUpdate(SchedulerClientType type) const;
-
-  // Notifies the delegate about impression data update.
-  void NotifyImpressionUpdate();
-
-  // Finds an impression that needs to update based on notification id.
-  Impression* FindImpressionNeedsUpdate(const std::string& notification_guid);
-
-  void OnClickInternal(const std::string& notification_guid, bool update_db);
-  void OnButtonClickInternal(const std::string& notification_guid,
-                             ActionButtonType button_type,
-                             bool update_db);
-  void OnDismissInternal(const std::string& notification_guid, bool update_db);
-
-  // Impression history and global states for all notification scheduler
-  // clients.
-  ClientStates client_states_;
-
-  // Notification guid to Impression map.
-  std::map<std::string, Impression*> impression_map_;
-
-  // The storage that persists data.
-  std::unique_ptr<CollectionStore<ClientState>> store_;
-
-  // System configuration.
-  const SchedulerConfig& config_;
-
-  // Whether the impression tracker is successfully initialized.
-  bool initialized_;
-
-  // If the database needs an update when any of the impression data is updated.
-  std::map<SchedulerClientType, bool> need_update_db_;
-
-  Delegate* delegate_;
-
-  base::WeakPtrFactory<ImpressionHistoryTrackerImpl> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerImpl);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_HISTORY_TRACKER_H_
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc b/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc
deleted file mode 100644
index 0f06d1a..0000000
--- a/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright 2019 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/bind.h"
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/notifications/scheduler/impression_history_tracker.h"
-#include "chrome/browser/notifications/scheduler/test/test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using ::testing::Invoke;
-using StoreEntries = std::vector<std::unique_ptr<notifications::ClientState>>;
-
-namespace notifications {
-namespace {
-
-const char kGuid1[] = "guid1";
-
-struct TestCase {
-  // Input data that will be pushed to the target class.
-  std::vector<test::ImpressionTestData> input;
-
-  // Expected output data.
-  std::vector<test::ImpressionTestData> expected;
-};
-
-Impression CreateImpression(const base::Time& create_time,
-                            const std::string& guid) {
-  return {create_time,
-          UserFeedback::kNoFeedback,
-          ImpressionResult::kInvalid,
-          false /* integrated */,
-          SchedulerTaskTime::kMorning,
-          guid,
-          SchedulerClientType::kTest1};
-}
-
-TestCase CreateDefaultTestCase() {
-  TestCase test_case;
-  test_case.input = {{SchedulerClientType::kTest1,
-                      2 /* current_max_daily_show */,
-                      {},
-                      base::nullopt /* suppression_info */}};
-  test_case.expected = test_case.input;
-  return test_case;
-}
-
-class MockImpressionStore : public CollectionStore<ClientState> {
- public:
-  MockImpressionStore() {}
-
-  MOCK_METHOD1(InitAndLoad, void(CollectionStore<ClientState>::LoadCallback));
-  MOCK_METHOD3(Add,
-               void(const std::string&,
-                    const ClientState&,
-                    base::OnceCallback<void(bool)>));
-  MOCK_METHOD3(Update,
-               void(const std::string&,
-                    const ClientState&,
-                    base::OnceCallback<void(bool)>));
-  MOCK_METHOD2(Delete,
-               void(const std::string&, base::OnceCallback<void(bool)>));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockImpressionStore);
-};
-
-class MockDelegate : public ImpressionHistoryTracker::Delegate {
- public:
-  MockDelegate() = default;
-  ~MockDelegate() = default;
-  MOCK_METHOD0(OnImpressionUpdated, void());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockDelegate);
-};
-
-// TODO(xingliu): Add more test cases following the test doc.
-class ImpressionHistoryTrackerTest : public ::testing::Test {
- public:
-  ImpressionHistoryTrackerTest() : store_(nullptr), delegate_(nullptr) {}
-  ~ImpressionHistoryTrackerTest() override = default;
-
-  void SetUp() override {
-    config_.impression_expiration = base::TimeDelta::FromDays(28);
-    config_.suppression_duration = base::TimeDelta::FromDays(56);
-  }
-
- protected:
-  // Creates the tracker and push in data.
-  void InitTrackerWithData(const TestCase& test_case) {
-    StoreEntries entries;
-    test::AddImpressionTestData(test_case.input, &entries);
-
-    auto store = std::make_unique<MockImpressionStore>();
-    store_ = store.get();
-    delegate_ = std::make_unique<MockDelegate>();
-    impression_trakcer_ = std::make_unique<ImpressionHistoryTrackerImpl>(
-        config_, std::move(store));
-
-    // Initialize the store and call the callback.
-    EXPECT_CALL(*store_, InitAndLoad(_))
-        .WillOnce(
-            Invoke([&entries](base::OnceCallback<void(bool, StoreEntries)> cb) {
-              std::move(cb).Run(true, std::move(entries));
-            }));
-    base::RunLoop loop;
-    impression_trakcer_->Init(
-        delegate_.get(), base::BindOnce(
-                             [](base::RepeatingClosure closure, bool success) {
-                               EXPECT_TRUE(success);
-                               std::move(closure).Run();
-                             },
-                             loop.QuitClosure()));
-    loop.Run();
-  }
-
-  // Verifies the |expected_test_data| matches the internal states.
-  void VerifyClientStates(const TestCase& test_case) {
-    std::map<SchedulerClientType, const ClientState*> client_states;
-    impression_trakcer_->GetClientStates(&client_states);
-
-    ImpressionHistoryTracker::ClientStates expected_client_states;
-    test::AddImpressionTestData(test_case.expected, &expected_client_states);
-
-    DCHECK_EQ(expected_client_states.size(), client_states.size());
-    for (const auto& expected : expected_client_states) {
-      auto output_it = client_states.find(expected.first);
-      DCHECK(output_it != client_states.end());
-      EXPECT_EQ(*expected.second, *output_it->second)
-          << "Unmatch client states: \n"
-          << "Expected: \n"
-          << expected.second->DebugPrint() << " \n"
-          << "Acutual: \n"
-          << output_it->second->DebugPrint();
-    }
-  }
-
-  const SchedulerConfig& config() const { return config_; }
-  MockImpressionStore* store() { return store_; }
-  MockDelegate* delegate() { return delegate_.get(); }
-  ImpressionHistoryTracker* tracker() { return impression_trakcer_.get(); }
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  SchedulerConfig config_;
-  std::unique_ptr<ImpressionHistoryTracker> impression_trakcer_;
-  MockImpressionStore* store_;
-  std::unique_ptr<MockDelegate> delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerTest);
-};
-
-// Verifies expired impression will be deleted.
-TEST_F(ImpressionHistoryTrackerTest, DeleteExpiredImpression) {
-  TestCase test_case;
-  auto expired_create_time = base::Time::Now() - base::TimeDelta::FromDays(1) -
-                             config().impression_expiration;
-  auto not_expired_time = base::Time::Now() + base::TimeDelta::FromDays(1) -
-                          config().impression_expiration;
-  Impression expired{expired_create_time,         UserFeedback::kNoFeedback,
-                     ImpressionResult::kInvalid,  false /* integrated */,
-                     SchedulerTaskTime::kMorning, "guid1",
-                     SchedulerClientType::kTest1};
-  Impression not_expired{not_expired_time,
-                         UserFeedback::kNoFeedback,
-                         ImpressionResult::kInvalid,
-                         false /* integrated */,
-                         SchedulerTaskTime::kMorning,
-                         "guid2",
-                         SchedulerClientType::kTest1};
-
-  // The impressions in the input should be sorted by creation time when gets
-  // loaded to memory.
-  test_case.input = {{SchedulerClientType::kTest1,
-                      2 /* current_max_daily_show */,
-                      {expired, not_expired, expired},
-                      base::nullopt /* suppression_info */}};
-
-  // Expired impression created in |expired_create_time| should be deleted.
-  // No change expected on the next impression, which is not expired and no user
-  // feedback .
-  test_case.expected = {{SchedulerClientType::kTest1,
-                         2 /* current_max_daily_show */,
-                         {not_expired},
-                         base::nullopt /* suppression_info */}};
-
-  InitTrackerWithData(test_case);
-  EXPECT_CALL(*store(), Update(_, _, _));
-  EXPECT_CALL(*delegate(), OnImpressionUpdated());
-  tracker()->AnalyzeImpressionHistory();
-  VerifyClientStates(test_case);
-}
-
-// If impression has been deleted, click should have no result.
-TEST_F(ImpressionHistoryTrackerTest, ClickNoImpression) {
-  TestCase test_case = CreateDefaultTestCase();
-  InitTrackerWithData(test_case);
-  EXPECT_CALL(*store(), Update(_, _, _)).Times(0);
-  EXPECT_CALL(*delegate(), OnImpressionUpdated()).Times(0);
-  tracker()->OnClick(kGuid1);
-  VerifyClientStates(test_case);
-}
-
-struct UserActionTestParam {
-  ImpressionResult impression_result = ImpressionResult::kInvalid;
-  UserFeedback user_feedback = UserFeedback::kNoFeedback;
-  int current_max_daily_show = 0;
-  base::Optional<ActionButtonType> button_type;
-  base::Optional<SuppressionInfo> suppression_info;
-};
-
-class ImpressionHistoryTrackerUserActionTest
-    : public ImpressionHistoryTrackerTest,
-      public ::testing::WithParamInterface<UserActionTestParam> {
- public:
-  ImpressionHistoryTrackerUserActionTest() = default;
-  ~ImpressionHistoryTrackerUserActionTest() override = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerUserActionTest);
-};
-
-// TODO(xingliu): Add test for unhelpful/dismiss, need to use base::Clock to
-// mock base::Time::Now().
-const UserActionTestParam kUserActionTestParams[] = {
-    {ImpressionResult::kPositive, UserFeedback::kClick, 3, base::nullopt,
-     base::nullopt},
-    {ImpressionResult::kPositive, UserFeedback::kHelpful, 3,
-     ActionButtonType::kHelpful, base::nullopt}};
-
-// User actions like clicks should update the ClientState data accordingly.
-TEST_P(ImpressionHistoryTrackerUserActionTest, UserAction) {
-  TestCase test_case = CreateDefaultTestCase();
-  Impression impression = CreateImpression(base::Time::Now(), kGuid1);
-  DCHECK(!test_case.input.empty());
-  test_case.input.front().impressions.emplace_back(impression);
-
-  impression.impression = GetParam().impression_result;
-  impression.integrated = true;
-  impression.feedback = GetParam().user_feedback;
-
-  test_case.expected.front().current_max_daily_show =
-      GetParam().current_max_daily_show;
-  test_case.expected.front().impressions.emplace_back(impression);
-  test_case.expected.front().suppression_info = GetParam().suppression_info;
-
-  InitTrackerWithData(test_case);
-  EXPECT_CALL(*store(), Update(_, _, _));
-  EXPECT_CALL(*delegate(), OnImpressionUpdated());
-
-  // Trigger user action.
-  if (GetParam().user_feedback == UserFeedback::kClick)
-    tracker()->OnClick(kGuid1);
-  else if (GetParam().button_type.has_value())
-    tracker()->OnActionClick(kGuid1, GetParam().button_type.value());
-
-  VerifyClientStates(test_case);
-}
-
-INSTANTIATE_TEST_SUITE_P(,
-                         ImpressionHistoryTrackerUserActionTest,
-                         testing::ValuesIn(kUserActionTestParams));
-
-}  // namespace
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_store.cc b/chrome/browser/notifications/scheduler/impression_store.cc
deleted file mode 100644
index 8b79c62..0000000
--- a/chrome/browser/notifications/scheduler/impression_store.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/impression_store.h"
-
-#include "chrome/browser/notifications/scheduler/proto_conversion.h"
-
-namespace leveldb_proto {
-
-void DataToProto(notifications::ClientState* client_state,
-                 notifications::proto::ClientState* proto) {
-  ClientStateToProto(client_state, proto);
-}
-
-void ProtoToData(notifications::proto::ClientState* proto,
-                 notifications::ClientState* client_state) {
-  ClientStateFromProto(proto, client_state);
-}
-
-}  // namespace leveldb_proto
-
-namespace notifications {
-
-ImpressionStore::ImpressionStore(
-    std::unique_ptr<
-        leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db)
-    : db_(std::move(db)), weak_ptr_factory_(this) {}
-
-ImpressionStore::~ImpressionStore() = default;
-
-void ImpressionStore::InitAndLoad(LoadCallback callback) {
-  db_->Init(base::BindOnce(&ImpressionStore::OnDbInitialized,
-                           weak_ptr_factory_.GetWeakPtr(),
-                           std::move(callback)));
-}
-
-void ImpressionStore::OnDbInitialized(LoadCallback callback,
-                                      leveldb_proto::Enums::InitStatus status) {
-  if (status != leveldb_proto::Enums::InitStatus::kOK) {
-    std::move(callback).Run(false, Entries());
-    return;
-  }
-
-  // Load the data after a successful initialization.
-  db_->LoadEntries(base::BindOnce(&ImpressionStore::OnDataLoaded,
-                                  weak_ptr_factory_.GetWeakPtr(),
-                                  std::move(callback)));
-}
-
-void ImpressionStore::OnDataLoaded(LoadCallback callback,
-                                   bool success,
-                                   std::unique_ptr<EntryVector> entry_vector) {
-  // The database failed to load.
-  if (!success) {
-    std::move(callback).Run(false, Entries());
-    return;
-  }
-
-  // Success to load but no data.
-  if (!entry_vector) {
-    std::move(callback).Run(true, Entries());
-    return;
-  }
-
-  // Load data.
-  Entries entries;
-  for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) {
-    std::unique_ptr<ClientState> client_state = std::make_unique<ClientState>();
-    *client_state = std::move(*it);
-    entries.emplace_back(std::move(client_state));
-  }
-
-  std::move(callback).Run(true, std::move(entries));
-}
-
-void ImpressionStore::Add(const std::string& key,
-                          const ClientState& client_state,
-                          UpdateCallback callback) {
-  Update(key, client_state, std::move(callback));
-}
-
-void ImpressionStore::Update(const std::string& key,
-                             const ClientState& client_state,
-                             UpdateCallback callback) {
-  auto entries_to_save = std::make_unique<KeyEntryVector>();
-  entries_to_save->emplace_back(std::make_pair(key, client_state));
-  db_->UpdateEntries(std::move(entries_to_save),
-                     std::make_unique<KeyVector>() /*keys_to_remove*/,
-                     std::move(callback));
-}
-
-void ImpressionStore::Delete(const std::string& key, UpdateCallback callback) {
-  auto keys_to_delete = std::make_unique<KeyVector>();
-  keys_to_delete->emplace_back(key);
-  db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/,
-                     std::move(keys_to_delete), std::move(callback));
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_store.h b/chrome/browser/notifications/scheduler/impression_store.h
deleted file mode 100644
index 3749720a..0000000
--- a/chrome/browser/notifications/scheduler/impression_store.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/notifications/proto/client_state.pb.h"
-#include "chrome/browser/notifications/scheduler/collection_store.h"
-#include "chrome/browser/notifications/scheduler/impression_types.h"
-#include "components/leveldb_proto/public/proto_database.h"
-
-// Forward declaration for proto conversion.
-namespace leveldb_proto {
-void DataToProto(notifications::ClientState* client_state,
-                 notifications::proto::ClientState* proto);
-
-void ProtoToData(notifications::proto::ClientState* proto,
-                 notifications::ClientState* client_state);
-}  // namespace leveldb_proto
-
-namespace notifications {
-
-// An impression storage using a proto database to persist data.
-class ImpressionStore : public CollectionStore<ClientState> {
- public:
-  ImpressionStore(
-      std::unique_ptr<
-          leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db);
-  ~ImpressionStore() override;
-
- private:
-  using KeyEntryVector = std::vector<std::pair<std::string, ClientState>>;
-  using KeyVector = std::vector<std::string>;
-  using EntryVector = std::vector<ClientState>;
-
-  // CollectionStore implementation.
-  void InitAndLoad(LoadCallback callback) override;
-  void Add(const std::string& key,
-           const ClientState& client_state,
-           UpdateCallback callback) override;
-  void Update(const std::string& key,
-              const ClientState& client_state,
-              UpdateCallback callback) override;
-  void Delete(const std::string& key, UpdateCallback callback) override;
-
-  // Called when the proto database is initialized but no yet loading the data
-  // into memory.
-  void OnDbInitialized(LoadCallback callback,
-                       leveldb_proto::Enums::InitStatus status);
-
-  // Called after loading the data from database.
-  void OnDataLoaded(LoadCallback callback,
-                    bool success,
-                    std::unique_ptr<EntryVector> entry_vector);
-
-  std::unique_ptr<leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>>
-      db_;
-  base::WeakPtrFactory<ImpressionStore> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImpressionStore);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/impression_store_unittest.cc b/chrome/browser/notifications/scheduler/impression_store_unittest.cc
deleted file mode 100644
index 4872afe..0000000
--- a/chrome/browser/notifications/scheduler/impression_store_unittest.cc
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/impression_store.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/notifications/proto/client_state.pb.h"
-#include "chrome/browser/notifications/scheduler/impression_types.h"
-#include "chrome/browser/notifications/scheduler/proto_conversion.h"
-#include "components/leveldb_proto/public/proto_database.h"
-#include "components/leveldb_proto/testing/fake_db.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using leveldb_proto::test::FakeDB;
-using InitStatus = leveldb_proto::Enums::InitStatus;
-using Entries = notifications::ImpressionStore::Entries;
-using DbEntries = std::vector<notifications::ClientState>;
-using DbEntriesPtr = std::unique_ptr<std::vector<notifications::ClientState>>;
-using TestClientStates = std::map<std::string, notifications::ClientState>;
-
-namespace notifications {
-namespace {
-
-const char kClientStateKey[] = "guid_client_state_key1";
-const ClientState kDefaultClientState;
-
-// Test fixture to verify impression store.
-class ImpressionStoreTest : public testing::Test {
- public:
-  ImpressionStoreTest() : load_result_(false), db_(nullptr) {}
-  ~ImpressionStoreTest() override = default;
-
-  void SetUp() override {}
-
- protected:
-  // Initialize the store with test data.
-  void Init(const TestClientStates& test_data, InitStatus status) {
-    CreateTestProto(test_data);
-
-    auto db =
-        std::make_unique<FakeDB<proto::ClientState, ClientState>>(&db_entries_);
-    db_ = db.get();
-    store_ = std::make_unique<ImpressionStore>(std::move(db));
-    store_->InitAndLoad(base::BindOnce(&ImpressionStoreTest::OnEntriesLoaded,
-                                       base::Unretained(this)));
-    db_->InitStatusCallback(status);
-  }
-
-  bool load_result() const { return load_result_; }
-  const Entries& loaded_entries() const { return loaded_entries_; }
-  FakeDB<proto::ClientState, ClientState>* db() { return db_; }
-
-  CollectionStore<ClientState>* store() { return store_.get(); }
-
-  void OnEntriesLoaded(bool success, Entries entries) {
-    loaded_entries_ = std::move(entries);
-    load_result_ = success;
-  }
-
-  // Verifies the entries in the db is |expected|.
-  void VerifyDataInDb(DbEntriesPtr expected) {
-    db_->LoadEntries(base::BindOnce(
-        [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) {
-          EXPECT_TRUE(success);
-          DCHECK(entries);
-          DCHECK(expected);
-          EXPECT_EQ(entries->size(), expected->size());
-          for (size_t i = 0, size = entries->size(); i < size; ++i) {
-            EXPECT_EQ((*entries)[i], (*expected)[i]);
-          }
-        },
-        std::move(expected)));
-    db_->LoadCallback(true);
-  }
-
- private:
-  void CreateTestProto(const TestClientStates& client_states) {
-    for (const auto& pair : client_states) {
-      auto client_state(pair.second);
-      auto key = pair.first;
-      notifications::proto::ClientState proto;
-      ClientStateToProto(&client_state, &proto);
-      db_entries_.emplace(key, proto);
-    }
-  }
-
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  std::map<std::string, proto::ClientState> db_entries_;
-  bool load_result_;
-  Entries loaded_entries_;
-
-  FakeDB<proto::ClientState, ClientState>* db_;
-  std::unique_ptr<CollectionStore<ClientState>> store_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImpressionStoreTest);
-};
-
-// Initializes an empty database should success.
-TEST_F(ImpressionStoreTest, InitSuccessEmptyDb) {
-  Init(TestClientStates(), InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-  EXPECT_TRUE(loaded_entries().empty());
-}
-
-// Initialize non-empty database should success.
-TEST_F(ImpressionStoreTest, InitSuccessWithData) {
-  auto test_data = TestClientStates();
-  test_data.emplace(kClientStateKey, kDefaultClientState);
-  Init(test_data, InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-  EXPECT_EQ(loaded_entries().size(), 1u);
-  EXPECT_EQ(*loaded_entries().back(), kDefaultClientState);
-}
-
-// Failure when loading the data will result in error.
-TEST_F(ImpressionStoreTest, InitSuccessLoadFailed) {
-  Init(TestClientStates(), InitStatus::kOK);
-  db()->LoadCallback(false);
-  EXPECT_EQ(load_result(), false);
-  EXPECT_TRUE(loaded_entries().empty());
-}
-
-// Failed database initialization will result in error.
-TEST_F(ImpressionStoreTest, InitFailed) {
-  Init(TestClientStates(), InitStatus::kCorrupt);
-  EXPECT_EQ(load_result(), false);
-  EXPECT_TRUE(loaded_entries().empty());
-}
-
-// Verifies adding data.
-TEST_F(ImpressionStoreTest, Add) {
-  Init(TestClientStates(), InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-  EXPECT_TRUE(loaded_entries().empty());
-
-  // Add data to the store.
-  store()->Add(kClientStateKey, kDefaultClientState,
-               base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
-  db()->UpdateCallback(true);
-
-  // Verify the new data is in the database.
-  auto expected = std::make_unique<DbEntries>();
-  expected->emplace_back(kDefaultClientState);
-  VerifyDataInDb(std::move(expected));
-}
-
-// Verifies failure when adding data will result in error.
-TEST_F(ImpressionStoreTest, AddFailed) {
-  Init(TestClientStates(), InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-  EXPECT_TRUE(loaded_entries().empty());
-
-  store()->Add(kClientStateKey, kDefaultClientState,
-               base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
-  db()->UpdateCallback(false);
-}
-
-// Verifies updating data.
-TEST_F(ImpressionStoreTest, Update) {
-  auto test_data = TestClientStates();
-  test_data.emplace(kClientStateKey, kDefaultClientState);
-  Init(test_data, InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-
-  ClientState new_client_state;
-  new_client_state.current_max_daily_show = 100;
-
-  // Update the database.
-  store()->Update(kClientStateKey, new_client_state,
-                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
-  db()->UpdateCallback(true);
-
-  // Verify the updated data is in the database.
-  auto expected = std::make_unique<DbEntries>();
-  expected->emplace_back(new_client_state);
-  VerifyDataInDb(std::move(expected));
-}
-
-// Verifies deleting data.
-TEST_F(ImpressionStoreTest, Delete) {
-  auto test_data = TestClientStates();
-  test_data.emplace(kClientStateKey, kDefaultClientState);
-  Init(test_data, InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-
-  // Delete the entry.
-  store()->Delete(kClientStateKey,
-                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
-
-  // Verify there is no data in the database.
-  VerifyDataInDb(std::make_unique<DbEntries>());
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_types.cc b/chrome/browser/notifications/scheduler/impression_types.cc
deleted file mode 100644
index 12eeaf5..0000000
--- a/chrome/browser/notifications/scheduler/impression_types.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/impression_types.h"
-
-#include <sstream>
-
-#include "base/format_macros.h"
-#include "base/strings/stringprintf.h"
-
-namespace notifications {
-
-bool Impression::operator==(const Impression& other) const {
-  return create_time == other.create_time && feedback == other.feedback &&
-         impression == other.impression && integrated == other.integrated &&
-         task_start_time == other.task_start_time && guid == other.guid &&
-         type == other.type;
-}
-
-SuppressionInfo::SuppressionInfo(const base::Time& last_trigger,
-                                 const base::TimeDelta& duration)
-    : last_trigger_time(last_trigger), duration(duration), recover_goal(1) {}
-
-SuppressionInfo::SuppressionInfo(const SuppressionInfo& other) = default;
-
-bool SuppressionInfo::operator==(const SuppressionInfo& other) const {
-  return last_trigger_time == other.last_trigger_time &&
-         duration == other.duration && recover_goal == other.recover_goal;
-}
-
-base::Time SuppressionInfo::ReleaseTime() const {
-  return last_trigger_time + duration;
-}
-
-ClientState::ClientState()
-    : type(SchedulerClientType::kUnknown), current_max_daily_show(0) {}
-
-ClientState::ClientState(const ClientState& other) = default;
-
-ClientState::~ClientState() = default;
-
-bool ClientState::operator==(const ClientState& other) const {
-  return type == other.type &&
-         current_max_daily_show == other.current_max_daily_show &&
-         impressions == other.impressions &&
-         suppression_info == other.suppression_info;
-}
-
-std::string ClientState::DebugPrint() const {
-  std::string log = base::StringPrintf(
-      "Client state: type: %d \n"
-      "current_max_daily_show: %d \n"
-      "impressions.size(): %zu \n",
-      static_cast<int>(type), current_max_daily_show, impressions.size());
-
-  for (const auto& impression : impressions) {
-    std::ostringstream stream;
-    stream << "Impression, create_time:" << impression.create_time << "\n"
-           << " create_time in microseconds:"
-           << impression.create_time.ToDeltaSinceWindowsEpoch().InMicroseconds()
-           << "\n"
-           << "feedback: " << static_cast<int>(impression.feedback) << "\n"
-           << "impression result: " << static_cast<int>(impression.impression)
-           << " \n"
-           << "integrated: " << impression.integrated << "\n"
-           << "task start time: "
-           << static_cast<int>(impression.task_start_time) << "\n"
-           << "guid: " << impression.guid << "\n"
-           << "type: " << static_cast<int>(impression.type);
-    log += stream.str();
-  }
-
-  if (suppression_info.has_value()) {
-    std::ostringstream stream;
-    stream << "Suppression info, last_trigger_time:"
-           << suppression_info->last_trigger_time << "\n"
-           << "duration:" << suppression_info->duration << "\n"
-           << "recover_goal:" << suppression_info->recover_goal;
-    log += stream.str();
-  }
-
-  return log;
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_types.h b/chrome/browser/notifications/scheduler/impression_types.h
deleted file mode 100644
index 2399547..0000000
--- a/chrome/browser/notifications/scheduler/impression_types.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_IMPRESSION_TYPES_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_TYPES_H_
-
-#include <deque>
-#include <map>
-#include <string>
-
-#include "base/optional.h"
-#include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace notifications {
-
-// Contains data to determine when a notification should be shown to the user
-// and the user impression towards this notification.
-//
-// Life cycle:
-// 1. Created after the notification is shown to the user.
-// 2. |feedback| is set after the user interacts with the notification.
-// 3. Notification scheduler API consumer gets the user feedback and generates
-// an impression result, which may affect notification exposure.
-// 4. The impression is deleted after it expires.
-struct Impression {
-  bool operator==(const Impression& other) const;
-
-  // Creation timestamp.
-  base::Time create_time;
-
-  // The user feedback on the notification, each notification will have at most
-  // one feedback. Sets after the user interacts with the notification.
-  UserFeedback feedback = UserFeedback::kNoFeedback;
-
-  // The impression type. The client of a notification type takes one or several
-  // user feedbacks as input and generate a user impression, which will
-  // eventually affect the rate to deliver notifications to the user.
-  ImpressionResult impression = ImpressionResult::kInvalid;
-
-  // If the user feedback is used in computing the current notification deliver
-  // rate.
-  bool integrated = false;
-
-  // The task start time when this impression is generated.
-  SchedulerTaskTime task_start_time = SchedulerTaskTime::kUnknown;
-
-  // The unique identifier of the notification.
-  std::string guid;
-
-  // The type of the notification. Not persisted to disk, set after database
-  // initialized.
-  // TODO(xingliu): Consider to persist this as well.
-  SchedulerClientType type = SchedulerClientType::kUnknown;
-};
-
-// Contains details about supression and recovery after suppression expired.
-struct SuppressionInfo {
-  SuppressionInfo(const base::Time& last_trigger,
-                  const base::TimeDelta& duration);
-  SuppressionInfo(const SuppressionInfo& other);
-  ~SuppressionInfo() = default;
-  bool operator==(const SuppressionInfo& other) const;
-
-  // Time that the suppression should release.
-  base::Time ReleaseTime() const;
-
-  // The last supression trigger time.
-  base::Time last_trigger_time;
-
-  // The duration for the suppression.
-  base::TimeDelta duration;
-
-  // |current_max_daily_show| will change to this after the suppression
-  // expired.
-  int recover_goal;
-};
-
-// Stores the global states about how often the notification can be shown
-// to the user and the history of user interactions to a particular notification
-// client.
-struct ClientState {
-  using Impressions = std::deque<Impression>;
-  ClientState();
-  explicit ClientState(const ClientState& other);
-  ~ClientState();
-
-  bool operator==(const ClientState& other) const;
-
-  // Dumps data for debugging.
-  std::string DebugPrint() const;
-
-  // The type of notification using the scheduler.
-  SchedulerClientType type;
-
-  // The maximum number of notifications shown to the user for this type. May
-  // change if the user interacts with the notification.
-  int current_max_daily_show;
-
-  // A list of user impression history. Sorted by creation time.
-  Impressions impressions;
-
-  // Suppression details, no value if there is currently no suppression.
-  base::Optional<SuppressionInfo> suppression_info;
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/init_aware_scheduler.cc b/chrome/browser/notifications/scheduler/init_aware_scheduler.cc
deleted file mode 100644
index 965bf3a..0000000
--- a/chrome/browser/notifications/scheduler/init_aware_scheduler.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/init_aware_scheduler.h"
-
-#include "base/bind.h"
-#include "chrome/browser/notifications/scheduler/notification_params.h"
-
-namespace notifications {
-
-InitAwareNotificationScheduler::InitAwareNotificationScheduler(
-    std::unique_ptr<NotificationScheduler> impl)
-    : impl_(std::move(impl)), weak_ptr_factory_(this) {}
-
-InitAwareNotificationScheduler::~InitAwareNotificationScheduler() = default;
-
-void InitAwareNotificationScheduler::Init(InitCallback init_callback) {
-  DCHECK(!init_success_.has_value());
-  impl_->Init(base::BindOnce(&InitAwareNotificationScheduler::OnInitialized,
-                             weak_ptr_factory_.GetWeakPtr(),
-                             std::move(init_callback)));
-}
-void InitAwareNotificationScheduler::Schedule(
-    std::unique_ptr<NotificationParams> params) {
-  if (init_success_.has_value() && *init_success_) {
-    impl_->Schedule(std::move(params));
-    return;
-  }
-
-  if (init_success_.has_value() && !*init_success_)
-    return;
-
-  cached_closures_.emplace_back(
-      base::BindOnce(&InitAwareNotificationScheduler::Schedule,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(params)));
-}
-
-void InitAwareNotificationScheduler::OnInitialized(InitCallback init_callback,
-                                                   bool success) {
-  init_success_ = success;
-  if (!success) {
-    cached_closures_.clear();
-    std::move(init_callback).Run(false);
-    return;
-  }
-
-  // Flush all cached calls.
-  for (auto it = cached_closures_.begin(); it != cached_closures_.end(); ++it) {
-    std::move(*it).Run();
-  }
-  cached_closures_.clear();
-  std::move(init_callback).Run(true);
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/init_aware_scheduler.h b/chrome/browser/notifications/scheduler/init_aware_scheduler.h
deleted file mode 100644
index df8c61d..0000000
--- a/chrome/browser/notifications/scheduler/init_aware_scheduler.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INIT_AWARE_SCHEDULER_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INIT_AWARE_SCHEDULER_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler.h"
-
-namespace notifications {
-
-struct NotificationParams;
-
-// NotificationScheduler implementation that caches NotificationScheduler API
-// calls and flush them to the actual implementation after initialization
-// succeeded.
-class InitAwareNotificationScheduler : public NotificationScheduler {
- public:
-  explicit InitAwareNotificationScheduler(
-      std::unique_ptr<NotificationScheduler> impl);
-  ~InitAwareNotificationScheduler() override;
-
- private:
-  // NotificationScheduler implementation.
-  void Init(InitCallback init_callback) override;
-  void Schedule(
-      std::unique_ptr<NotificationParams> notification_params) override;
-
-  // Called after initialization is done.
-  void OnInitialized(InitCallback init_callback, bool success);
-
-  // Whether the initialization is successful. No value if initialization is not
-  // finished.
-  base::Optional<bool> init_success_;
-
-  // Cached calls.
-  std::vector<base::OnceClosure> cached_closures_;
-
-  // Actual implementation.
-  std::unique_ptr<NotificationScheduler> impl_;
-
-  base::WeakPtrFactory<InitAwareNotificationScheduler> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(InitAwareNotificationScheduler);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INIT_AWARE_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/init_aware_scheduler_unittest.cc b/chrome/browser/notifications/scheduler/init_aware_scheduler_unittest.cc
deleted file mode 100644
index 107be6a8..0000000
--- a/chrome/browser/notifications/scheduler/init_aware_scheduler_unittest.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/init_aware_scheduler.h"
-
-#include <memory>
-
-#include "base/bind_helpers.h"
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/notifications/scheduler/notification_params.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using testing::InSequence;
-using testing::Invoke;
-
-namespace notifications {
-namespace {
-
-class MockNotificationScheduler : public NotificationScheduler {
- public:
-  MockNotificationScheduler() = default;
-  ~MockNotificationScheduler() override = default;
-
-  MOCK_METHOD1(Init, void(InitCallback));
-  MOCK_METHOD1(Schedule, void(std::unique_ptr<NotificationParams>));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockNotificationScheduler);
-};
-
-class InitAwareNotificationSchedulerTest : public testing::Test {
- public:
-  InitAwareNotificationSchedulerTest() : scheduler_impl_(nullptr) {}
-  ~InitAwareNotificationSchedulerTest() override = default;
-
-  void SetUp() override {
-    auto scheduler = std::make_unique<MockNotificationScheduler>();
-    scheduler_impl_ = scheduler.get();
-    init_aware_scheduler_ =
-        std::make_unique<InitAwareNotificationScheduler>(std::move(scheduler));
-  }
-
- protected:
-  std::unique_ptr<NotificationParams> BuildParams() {
-    return std::make_unique<NotificationParams>(
-        SchedulerClientType::kUnknown, NotificationData(), ScheduleParams());
-  }
-
-  NotificationScheduler* init_aware_scheduler() {
-    return init_aware_scheduler_.get();
-  }
-  MockNotificationScheduler* scheduler_impl() { return scheduler_impl_; }
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  MockNotificationScheduler* scheduler_impl_;
-  std::unique_ptr<NotificationScheduler> init_aware_scheduler_;
-
-  DISALLOW_COPY_AND_ASSIGN(InitAwareNotificationSchedulerTest);
-};
-
-// Checks std::unique_ptr<NotificationParams> has specific guid.
-MATCHER_P(GuidIs, expected_guid, "") {
-  return arg->guid == expected_guid;
-}
-
-// Verifies cached calls are flushed into the actual implementation.
-TEST_F(InitAwareNotificationSchedulerTest, FlushCachedCalls) {
-  auto params = BuildParams();
-  std::string guid = params->guid;
-  EXPECT_FALSE(guid.empty());
-  {
-    InSequence sequence;
-    EXPECT_CALL(*scheduler_impl(), Init(_))
-        .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) {
-          std::move(cb).Run(true /*success*/);
-        }));
-    EXPECT_CALL(*scheduler_impl(), Schedule(GuidIs(guid)));
-
-    // Schedule() call before Init() will be cached.
-    init_aware_scheduler()->Schedule(std::move(params));
-    init_aware_scheduler()->Init(base::DoNothing());
-  }
-}
-
-// Verifies that API calls after successful initialization will call into the
-// actual implementation.
-TEST_F(InitAwareNotificationSchedulerTest, CallAfterInitSuccess) {
-  auto params = BuildParams();
-  std::string guid = params->guid;
-  EXPECT_FALSE(guid.empty());
-  {
-    InSequence sequence;
-    EXPECT_CALL(*scheduler_impl(), Init(_))
-        .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) {
-          std::move(cb).Run(true /*success*/);
-        }));
-    EXPECT_CALL(*scheduler_impl(), Schedule(GuidIs(guid)));
-
-    // Schedule() call after Init().
-    init_aware_scheduler()->Init(base::DoNothing());
-    init_aware_scheduler()->Schedule(std::move(params));
-  }
-}
-
-// Verifies no calls are flushed to actual implementation if initialization
-// failed.
-TEST_F(InitAwareNotificationSchedulerTest, NoFlushOnInitFailure) {
-  auto params1 = BuildParams();
-  auto params2 = BuildParams();
-
-  EXPECT_CALL(*scheduler_impl(), Init(_))
-      .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) {
-        std::move(cb).Run(false /*success*/);
-      }));
-  EXPECT_CALL(*scheduler_impl(), Schedule(_)).Times(0);
-
-  init_aware_scheduler()->Schedule(std::move(params1));
-  init_aware_scheduler()->Init(base::DoNothing());
-  init_aware_scheduler()->Schedule(std::move(params2));
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/BUILD.gn b/chrome/browser/notifications/scheduler/internal/BUILD.gn
new file mode 100644
index 0000000..c8638e0
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/BUILD.gn
@@ -0,0 +1,96 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/buildflag_header.gni")
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+}
+
+# Internal library that embedders should not directly depend on.
+source_set("lib") {
+  visibility = [
+    ":unit_tests",
+    "//chrome/browser/notifications/scheduler",
+    "//chrome/browser/notifications/scheduler:factory",
+    "//chrome/browser/notifications/scheduler/test:test_lib",
+  ]
+
+  sources = [
+    "background_task_coordinator.cc",
+    "background_task_coordinator.h",
+    "collection_store.h",
+    "display_decider.cc",
+    "display_decider.h",
+    "distribution_policy.cc",
+    "distribution_policy.h",
+    "icon_entry.cc",
+    "icon_entry.h",
+    "icon_store.cc",
+    "icon_store.h",
+    "impression_history_tracker.cc",
+    "impression_history_tracker.h",
+    "impression_store.cc",
+    "impression_store.h",
+    "impression_types.cc",
+    "impression_types.h",
+    "init_aware_scheduler.cc",
+    "init_aware_scheduler.h",
+    "notification_entry.cc",
+    "notification_entry.h",
+    "notification_schedule_service_impl.cc",
+    "notification_schedule_service_impl.h",
+    "notification_scheduler.cc",
+    "notification_scheduler.h",
+    "notification_scheduler_context.cc",
+    "notification_scheduler_context.h",
+    "notification_store.cc",
+    "notification_store.h",
+    "proto_conversion.cc",
+    "proto_conversion.h",
+    "scheduled_notification_manager.cc",
+    "scheduled_notification_manager.h",
+    "scheduler_config.cc",
+    "scheduler_config.h",
+    "scheduler_utils.cc",
+    "scheduler_utils.h",
+  ]
+
+  # This target should not depend on anything in //chrome/* except the proto library.
+  deps = [
+    "//base",
+    "//chrome/browser/notifications/proto",
+    "//chrome/browser/notifications/scheduler/public",
+    "//components/keyed_service/core",
+    "//components/leveldb_proto",
+    "//skia",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "background_task_coordinator_unittest.cc",
+    "display_decider_unittest.cc",
+    "distribution_policy_unittest.cc",
+    "icon_store_unittest.cc",
+    "impression_history_tracker_unittest.cc",
+    "impression_store_unittest.cc",
+    "init_aware_scheduler_unittest.cc",
+    "notification_store_unittest.cc",
+    "proto_conversion_unittest.cc",
+    "scheduled_notification_manager_unittest.cc",
+    "scheduler_utils_unittest.cc",
+  ]
+
+  deps = [
+    "//chrome/browser/notifications/proto",
+    "//chrome/browser/notifications/scheduler/internal:lib",
+    "//chrome/browser/notifications/scheduler/public",
+    "//chrome/browser/notifications/scheduler/test:test_lib",
+    "//components/leveldb_proto:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/notifications/scheduler/internal/background_task_coordinator.cc b/chrome/browser/notifications/scheduler/internal/background_task_coordinator.cc
new file mode 100644
index 0000000..95bfa6c
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/background_task_coordinator.cc
@@ -0,0 +1,165 @@
+// Copyright 2019 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/notifications/scheduler/internal/background_task_coordinator.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/numerics/ranges.h"
+#include "base/optional.h"
+#include "base/time/clock.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_types.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_utils.h"
+#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
+
+namespace notifications {
+namespace {
+
+class BackgroundTaskCoordinatorHelper {
+ public:
+  BackgroundTaskCoordinatorHelper(
+      NotificationBackgroundTaskScheduler* background_task,
+      const SchedulerConfig* config,
+      base::Clock* clock)
+      : background_task_(background_task), config_(config), clock_(clock) {}
+  ~BackgroundTaskCoordinatorHelper() = default;
+
+  void ScheduleBackgroundTask(
+      BackgroundTaskCoordinator::Notifications notifications,
+      BackgroundTaskCoordinator::ClientStates client_states,
+      SchedulerTaskTime task_start_time) {
+    if (notifications.empty()) {
+      background_task_->Cancel();
+      return;
+    }
+
+    std::map<SchedulerClientType, int> shown_per_type;
+    int shown_total = 0;
+    SchedulerClientType last_shown_type = SchedulerClientType::kUnknown;
+    NotificationsShownToday(client_states, &shown_per_type, &shown_total,
+                            &last_shown_type);
+    bool reach_max_today_all_type =
+        shown_total >= config_->max_daily_shown_all_type;
+    base::Time next_morning = NextMorning();
+    base::Time this_evening = ThisEvening();
+
+    for (const auto& pair : notifications) {
+      auto type = pair.first;
+      auto it = client_states.find(type);
+      if (pair.second.empty() || (it == client_states.end()))
+        continue;
+
+      const ClientState* client_state = it->second;
+
+      // Try to schedule on the day that suppression expires.
+      if (client_state->suppression_info.has_value()) {
+        const auto& suppression = client_state->suppression_info.value();
+        base::Time suppression_expire;
+        ToLocalHour(config_->morning_task_hour, suppression.ReleaseTime(),
+                    0 /*day_delta*/, &suppression_expire);
+        MaybeUpdateBackgroundTaskTime(
+            std::max(suppression_expire, next_morning));
+        continue;
+      }
+
+      // Has met the quota for this notification type or for all types, only can
+      // send more on next day.
+      bool reach_max_today =
+          shown_per_type[type] >= client_state->current_max_daily_show;
+      if (reach_max_today || reach_max_today_all_type) {
+        MaybeUpdateBackgroundTaskTime(next_morning);
+        continue;
+      }
+
+      switch (task_start_time) {
+        case SchedulerTaskTime::kMorning:
+          // Still can send more in the evening.
+          MaybeUpdateBackgroundTaskTime(this_evening);
+          break;
+        case SchedulerTaskTime::kEvening:
+          // Wait until the next calendar day.
+          MaybeUpdateBackgroundTaskTime(next_morning);
+          break;
+        case SchedulerTaskTime::kUnknown:
+          // TODO(xingliu): Support arbitrary time background task.
+          NOTIMPLEMENTED();
+          break;
+      }
+    }
+
+    ScheduleBackgroundTaskInternal(task_start_time);
+  }
+
+ private:
+  // Returns the morning background task time on the next day.
+  base::Time NextMorning() {
+    base::Time next_morning;
+    bool success = ToLocalHour(config_->morning_task_hour, clock_->Now(),
+                               1 /*day_delta*/, &next_morning);
+    DCHECK(success);
+    return next_morning;
+  }
+
+  // Returns the evening background task time on today.
+  base::Time ThisEvening() {
+    base::Time this_evening;
+    bool success = ToLocalHour(config_->evening_task_hour, clock_->Now(),
+                               0 /*day_delta*/, &this_evening);
+    DCHECK(success);
+    return this_evening;
+  }
+
+  void MaybeUpdateBackgroundTaskTime(const base::Time& time) {
+    if (!background_task_time_.has_value() ||
+        time < background_task_time_.value())
+      background_task_time_ = time;
+  }
+
+  void ScheduleBackgroundTaskInternal(SchedulerTaskTime task_start_time) {
+    if (!background_task_time_.has_value())
+      return;
+
+    base::TimeDelta window_start_time =
+        background_task_time_.value() - clock_->Now();
+    window_start_time = base::ClampToRange(window_start_time, base::TimeDelta(),
+                                           base::TimeDelta::Max());
+
+    background_task_->Schedule(
+        task_start_time, window_start_time,
+        window_start_time + config_->background_task_window_duration);
+  }
+
+  NotificationBackgroundTaskScheduler* background_task_;
+  const SchedulerConfig* config_;
+  base::Clock* clock_;
+  base::Optional<base::Time> background_task_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorHelper);
+};
+
+}  // namespace
+
+BackgroundTaskCoordinator::BackgroundTaskCoordinator(
+    std::unique_ptr<NotificationBackgroundTaskScheduler> background_task,
+    const SchedulerConfig* config,
+    base::Clock* clock)
+    : background_task_(std::move(background_task)),
+      config_(config),
+      clock_(clock) {}
+
+BackgroundTaskCoordinator::~BackgroundTaskCoordinator() = default;
+
+void BackgroundTaskCoordinator::ScheduleBackgroundTask(
+    Notifications notifications,
+    ClientStates client_states,
+    SchedulerTaskTime task_start_time) {
+  auto helper = std::make_unique<BackgroundTaskCoordinatorHelper>(
+      background_task_.get(), config_, clock_);
+  helper->ScheduleBackgroundTask(std::move(notifications),
+                                 std::move(client_states), task_start_time);
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/background_task_coordinator.h b/chrome/browser/notifications/scheduler/internal/background_task_coordinator.h
new file mode 100644
index 0000000..f2001d2
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/background_task_coordinator.h
@@ -0,0 +1,59 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_BACKGROUND_TASK_COORDINATOR_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_BACKGROUND_TASK_COORDINATOR_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace base {
+class Clock;
+}  // namespace base
+
+namespace notifications {
+
+class NotificationBackgroundTaskScheduler;
+struct ClientState;
+struct NotificationEntry;
+struct SchedulerConfig;
+
+// Schedules background task at the right time based on scheduled notification
+// data and impression data.
+class BackgroundTaskCoordinator {
+ public:
+  using Notifications =
+      std::map<SchedulerClientType, std::vector<const NotificationEntry*>>;
+  using ClientStates = std::map<SchedulerClientType, const ClientState*>;
+  BackgroundTaskCoordinator(
+      std::unique_ptr<NotificationBackgroundTaskScheduler> background_task,
+      const SchedulerConfig* config,
+      base::Clock* clock);
+  virtual ~BackgroundTaskCoordinator();
+
+  // Schedule background task based on current notification in the storage.
+  virtual void ScheduleBackgroundTask(Notifications notifications,
+                                      ClientStates client_states,
+                                      SchedulerTaskTime task_start_time);
+
+ private:
+  // The class that actually schedules platform background task.
+  std::unique_ptr<NotificationBackgroundTaskScheduler> background_task_;
+
+  // System configuration.
+  const SchedulerConfig* config_;
+
+  // Clock to query the current timestamp.
+  base::Clock* clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinator);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_BACKGROUND_TASK_COORDINATOR_H_
diff --git a/chrome/browser/notifications/scheduler/internal/background_task_coordinator_unittest.cc b/chrome/browser/notifications/scheduler/internal/background_task_coordinator_unittest.cc
new file mode 100644
index 0000000..28d15a3d
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/background_task_coordinator_unittest.cc
@@ -0,0 +1,337 @@
+// Copyright 2019 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/notifications/scheduler/internal/background_task_coordinator.h"
+
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/test/scoped_task_environment.h"
+#include "base/time/clock.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
+#include "chrome/browser/notifications/scheduler/test/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace notifications {
+namespace {
+
+using Notifications = BackgroundTaskCoordinator::Notifications;
+using ClientStates = BackgroundTaskCoordinator::ClientStates;
+
+const char kGuid[] = "1234";
+const std::vector<test::ImpressionTestData> kSingleClientImpressionTestData = {
+    {SchedulerClientType::kTest1,
+     1 /* current_max_daily_show */,
+     {},
+     base::nullopt /* suppression_info */}};
+
+const std::vector<test::ImpressionTestData> kClientsImpressionTestData = {
+    {SchedulerClientType::kTest1,
+     1 /* current_max_daily_show */,
+     {},
+     base::nullopt /* suppression_info */},
+    {SchedulerClientType::kTest2,
+     2 /* current_max_daily_show */,
+     {},
+     base::nullopt /* suppression_info */}};
+
+class MockNotificationBackgroundTaskScheduler
+    : public NotificationBackgroundTaskScheduler {
+ public:
+  MockNotificationBackgroundTaskScheduler() = default;
+  ~MockNotificationBackgroundTaskScheduler() override = default;
+  MOCK_METHOD3(Schedule,
+               void(notifications::SchedulerTaskTime,
+                    base::TimeDelta,
+                    base::TimeDelta));
+  MOCK_METHOD0(Cancel, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockNotificationBackgroundTaskScheduler);
+};
+
+// Clock to mock Clock::Now() to get a fixed time in the test.
+class FakeClock : public base::Clock {
+ public:
+  FakeClock() = default;
+  ~FakeClock() override = default;
+
+  void SetTime(const base::Time& time) { time_ = time; }
+
+ private:
+  // base::Clock implementation.
+  base::Time Now() const override { return time_; }
+
+  base::Time time_;
+  DISALLOW_COPY_AND_ASSIGN(FakeClock);
+};
+
+struct TestData {
+  // Impression data as the input.
+  std::vector<test::ImpressionTestData> impression_test_data;
+
+  // Notification entries as the input.
+  std::vector<NotificationEntry> notification_entries;
+
+  // The type of current background task.
+  SchedulerTaskTime task_start_time = SchedulerTaskTime::kMorning;
+};
+
+class BackgroundTaskCoordinatorTest : public testing::Test {
+ public:
+  BackgroundTaskCoordinatorTest() = default;
+  ~BackgroundTaskCoordinatorTest() override = default;
+
+ protected:
+  void SetUp() override {
+    // Setup configuration used by this test.
+    config_.morning_task_hour = 6;
+    config_.evening_task_hour = 18;
+    config_.max_daily_shown_all_type = 3;
+    config_.max_daily_shown_per_type = 2;
+    config_.suppression_duration = base::TimeDelta::FromDays(3);
+
+    auto background_task =
+        std::make_unique<MockNotificationBackgroundTaskScheduler>();
+    background_task_ = background_task.get();
+    coordinator_ = std::make_unique<BackgroundTaskCoordinator>(
+        std::move(background_task), &config_, &clock_);
+  }
+
+  MockNotificationBackgroundTaskScheduler* background_task() {
+    return background_task_;
+  }
+
+  SchedulerConfig* config() { return &config_; }
+
+  void SetNow(const char* now_str) {
+    base::Time now = GetTime(now_str);
+    clock_.SetTime(now);
+  }
+
+  base::Time GetTime(const char* time_str) {
+    base::Time time;
+    bool success = base::Time::FromString(time_str, &time);
+    DCHECK(success);
+    return time;
+  }
+
+  void ScheduleTask(const TestData& test_data) {
+    test_data_ = test_data;
+    test::AddImpressionTestData(test_data_.impression_test_data,
+                                &client_states_);
+    std::map<SchedulerClientType, const ClientState*> client_states;
+    for (const auto& type : client_states_) {
+      client_states.emplace(type.first, type.second.get());
+    }
+
+    Notifications notifications;
+    for (const auto& entry : test_data_.notification_entries) {
+      notifications[entry.type].emplace_back(&entry);
+    }
+    coordinator_->ScheduleBackgroundTask(std::move(notifications),
+                                         std::move(client_states),
+                                         test_data_.task_start_time);
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  FakeClock clock_;
+  SchedulerConfig config_;
+  std::unique_ptr<BackgroundTaskCoordinator> coordinator_;
+  MockNotificationBackgroundTaskScheduler* background_task_;
+  TestData test_data_;
+  std::map<SchedulerClientType, std::unique_ptr<ClientState>> client_states_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorTest);
+};
+
+// No notification persisted, then no background task needs to be scheduled.
+// And current task should be canceled.
+TEST_F(BackgroundTaskCoordinatorTest, NoNotification) {
+  EXPECT_CALL(*background_task(), Cancel());
+  EXPECT_CALL(*background_task(), Schedule(_, _, _)).Times(0);
+  TestData test_data;
+  test_data.impression_test_data = kSingleClientImpressionTestData;
+  ScheduleTask(test_data);
+}
+
+// In a morning task, find one notification and schedule an evening task.
+TEST_F(BackgroundTaskCoordinatorTest, InMorningScheduleEvening) {
+  const char kNow[] = "04/25/20 01:00:00 AM";
+  SetNow(kNow);
+  EXPECT_CALL(*background_task(), Cancel()).Times(0);
+  // Expected to run task this evening.
+  auto expected_window_start = GetTime("04/25/20 18:00:00 PM") - GetTime(kNow);
+  EXPECT_CALL(*background_task(),
+              Schedule(_, expected_window_start,
+                       expected_window_start +
+                           config()->background_task_window_duration));
+
+  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
+  TestData test_data{
+      kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kMorning};
+  ScheduleTask(test_data);
+}
+
+// In morning task, schedule evening task but throttled, schedule to next
+// morning.
+TEST_F(BackgroundTaskCoordinatorTest, InMorningScheduleEveningThrottled) {
+  const char kNow[] = "04/25/20 02:00:00 PM";
+  SetNow(kNow);
+
+  EXPECT_CALL(*background_task(), Cancel()).Times(0);
+  // Expected to run task next morning.
+  EXPECT_CALL(*background_task(),
+              Schedule(_, GetTime("04/26/20 06:00:00 AM") - GetTime(kNow), _));
+
+  auto impression_data = kSingleClientImpressionTestData;
+  Impression impression;
+  impression.create_time = GetTime("04/25/20 01:00:00 AM");
+  impression_data.back().impressions.emplace_back(impression);
+
+  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
+  TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning};
+  ScheduleTask(test_data);
+}
+
+// In an evening task, schedule background task to run next morning.
+TEST_F(BackgroundTaskCoordinatorTest, InEveningScheduleNextMorning) {
+  const char kNow[] = "04/25/20 18:00:00 PM";
+  SetNow(kNow);
+  EXPECT_CALL(*background_task(), Cancel()).Times(0);
+  // Expected to run task next morning.
+  auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow);
+  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
+
+  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
+  TestData test_data{
+      kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kEvening};
+  ScheduleTask(test_data);
+}
+
+// In an evening task, schedule background task to run next morning, even if we
+// reached the daily max.
+TEST_F(BackgroundTaskCoordinatorTest, InEveningScheduleNextMorningThrottled) {
+  const char kNow[] = "04/25/20 18:00:00 PM";
+  SetNow(kNow);
+  EXPECT_CALL(*background_task(), Cancel()).Times(0);
+  // Expected to run task next morning.
+  auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow);
+  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
+
+  // We have reached daily max.
+  auto impression_data = kSingleClientImpressionTestData;
+  Impression impression;
+  impression.create_time = GetTime("04/25/20 01:00:00 AM");
+  impression_data.back().impressions.emplace_back(impression);
+
+  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
+  TestData test_data{
+      kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kEvening};
+  ScheduleTask(test_data);
+}
+
+// Suppression will result in background task scheduled after suppression
+// expired.
+TEST_F(BackgroundTaskCoordinatorTest, Suppression) {
+  const char kNow[] = "04/25/20 06:00:00 AM";
+  SetNow(kNow);
+  EXPECT_CALL(*background_task(), Cancel()).Times(0);
+  // Expected to run task in the morning after suppression expired.
+  auto expected_window_start = GetTime("04/28/20 06:00:00 AM") - GetTime(kNow);
+  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
+
+  auto impression_data = kSingleClientImpressionTestData;
+  impression_data.back().suppression_info = SuppressionInfo(
+      GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3));
+
+  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
+  TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning};
+  ScheduleTask(test_data);
+}
+
+// If two different types want to schedule at different times, pick the earilier
+// one.
+TEST_F(BackgroundTaskCoordinatorTest, ScheduleEarlierTime) {
+  const char kNow[] = "04/25/20 01:00:00 AM";
+  SetNow(kNow);
+  EXPECT_CALL(*background_task(), Cancel()).Times(0);
+  // kTest1 type will run this evening, kTest2 will run task 3 days later.
+  // Expected to run the earilier task.
+  auto expected_window_start = GetTime("04/25/20 18:00:00 PM") - GetTime(kNow);
+  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
+
+  NotificationEntry entry1(SchedulerClientType::kTest1, kGuid);
+  NotificationEntry entry2(SchedulerClientType::kTest2, "guid_entry2");
+  auto impression_data = kClientsImpressionTestData;
+  impression_data[0].suppression_info = SuppressionInfo(
+      GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3));
+  TestData test_data{
+      impression_data, {entry1, entry2}, SchedulerTaskTime::kMorning};
+  ScheduleTask(test_data);
+}
+
+// If reached |max_daily_shown_all_type|, background task should run tomorrow.
+TEST_F(BackgroundTaskCoordinatorTest, InMorningThrottledAllTypes) {
+  const char kNow[] = "04/25/20 05:00:00 AM";
+  SetNow(kNow);
+  EXPECT_CALL(*background_task(), Cancel()).Times(0);
+  // Expected to run task next morning.
+  auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow);
+  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
+
+  auto impression_data = kClientsImpressionTestData;
+  Impression impression;
+  impression.create_time = GetTime("04/25/20 01:00:00 AM");
+
+  // Make sure we reach daily max for all types.
+  for (int i = 0; i < config()->max_daily_shown_all_type; i++)
+    impression_data.back().impressions.emplace_back(impression);
+
+  NotificationEntry entry(SchedulerClientType::kTest1, kGuid);
+  TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning};
+  ScheduleTask(test_data);
+}
+
+// If reached |max_daily_shown_all_type| and all types have suppression,
+// background task should run after one suppression expired.
+TEST_F(BackgroundTaskCoordinatorTest, ThrottledAllTypesAndSuppression) {
+  const char kNow[] = "04/25/20 05:00:00 AM";
+  SetNow(kNow);
+  EXPECT_CALL(*background_task(), Cancel()).Times(0);
+  // Expected to run after 3 days suppression ends.
+  auto expected_window_start = GetTime("04/28/20 06:00:00 AM") - GetTime(kNow);
+  EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _));
+
+  auto impression_data = kClientsImpressionTestData;
+  Impression impression;
+  impression.create_time = GetTime("04/25/20 01:00:00 AM");
+
+  // Make sure we reach daily max for all types.
+  for (int i = 0; i < config()->max_daily_shown_all_type; i++)
+    impression_data[1].impressions.emplace_back(impression);
+
+  // Suppression for both types.
+  impression_data[0].suppression_info = SuppressionInfo(
+      GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3));
+  impression_data[1].suppression_info = SuppressionInfo(
+      GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(4));
+
+  NotificationEntry entry1(SchedulerClientType::kTest1, "test_guid_1");
+  NotificationEntry entry2(SchedulerClientType::kTest2, "test_guid_2");
+  TestData test_data{
+      impression_data, {entry1, entry2}, SchedulerTaskTime::kMorning};
+  ScheduleTask(test_data);
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/collection_store.h b/chrome/browser/notifications/scheduler/internal/collection_store.h
new file mode 100644
index 0000000..7a1fdf0
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/collection_store.h
@@ -0,0 +1,53 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_COLLECTION_STORE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_COLLECTION_STORE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+
+namespace notifications {
+
+// A storage interface which loads a collection of data type T into memory
+// during initialization. When updating the data, T will be copied to the actual
+// storage layer since the caller will keep in memory data as well.
+template <typename T>
+class CollectionStore {
+ public:
+  using Entries = std::vector<std::unique_ptr<T>>;
+  using LoadCallback = base::OnceCallback<void(bool, Entries)>;
+  using InitCallback = base::OnceCallback<void(bool)>;
+  using UpdateCallback = base::OnceCallback<void(bool)>;
+
+  // Initializes the database and loads all entries into memory.
+  virtual void InitAndLoad(LoadCallback callback) = 0;
+
+  // Adds an entry to the storage.
+  virtual void Add(const std::string& key,
+                   const T& entry,
+                   UpdateCallback callback) = 0;
+
+  // Updates an entry.
+  virtual void Update(const std::string& key,
+                      const T& entry,
+                      UpdateCallback callback) = 0;
+
+  // Deletes an entry from storage.
+  virtual void Delete(const std::string& key, UpdateCallback callback) = 0;
+
+  CollectionStore() = default;
+  virtual ~CollectionStore() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CollectionStore);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_COLLECTION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/internal/display_decider.cc b/chrome/browser/notifications/scheduler/internal/display_decider.cc
new file mode 100644
index 0000000..6e7a8e0
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/display_decider.cc
@@ -0,0 +1,190 @@
+// Copyright 2019 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/notifications/scheduler/internal/display_decider.h"
+
+#include <algorithm>
+
+#include "chrome/browser/notifications/scheduler/internal/distribution_policy.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_types.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_utils.h"
+
+using Notifications = notifications::DisplayDecider::Notifications;
+using Results = notifications::DisplayDecider::Results;
+using ClientStates = notifications::DisplayDecider::ClientStates;
+
+namespace notifications {
+namespace {
+
+// Helper class contains the actual logic to decide which notifications to show.
+// This is an one-shot class, callers should create a new object each time.
+class DecisionHelper {
+ public:
+  DecisionHelper(const SchedulerConfig* config,
+                 const std::vector<SchedulerClientType>& clients,
+                 std::unique_ptr<DistributionPolicy> distribution_policy,
+                 SchedulerTaskTime task_start_time,
+                 Notifications notifications,
+                 ClientStates client_states)
+      : notifications_(std::move(notifications)),
+        current_task_start_time_(task_start_time),
+        client_states_(std::move(client_states)),
+        config_(config),
+        clients_(clients),
+        policy_(std::move(distribution_policy)),
+        daily_max_to_show_all_types_(0),
+        last_shown_type_(SchedulerClientType::kUnknown),
+        shown_(0) {}
+
+  ~DecisionHelper() = default;
+
+  // Figures out a list of notifications to show.
+  void DecideNotificationToShow(Results* results) {
+    ComputeDailyQuotaAllTypes();
+    CountNotificationsShownToday();
+    PickNotificationToShow(results);
+  }
+
+ private:
+  void ComputeDailyQuotaAllTypes() {
+    // Only background task launch needs to comply to |policy_|.
+    if (current_task_start_time_ == SchedulerTaskTime::kUnknown) {
+      daily_max_to_show_all_types_ = config_->max_daily_shown_all_type;
+      return;
+    }
+
+    int quota = std::max(config_->max_daily_shown_all_type - shown_, 0);
+    DCHECK(policy_);
+    daily_max_to_show_all_types_ =
+        std::min(config_->max_daily_shown_all_type,
+                 policy_->MaxToShow(current_task_start_time_, quota));
+  }
+
+  void CountNotificationsShownToday() {
+    for (const auto& pair : client_states_) {
+      auto type = pair.first;
+      // TODO(xingliu): Ensure deprecated clients will not have data in storage.
+      DCHECK(std::find(clients_.begin(), clients_.end(), type) !=
+             clients_.end());
+    }
+
+    NotificationsShownToday(client_states_, &shown_per_type_, &shown_,
+                            &last_shown_type_);
+  }
+
+  // Picks a list of notifications to show.
+  void PickNotificationToShow(Results* to_show) {
+    DCHECK(to_show);
+    if (shown_ > config_->max_daily_shown_all_type || clients_.empty())
+      return;
+
+    // No previous shown notification, move the iterator to last element.
+    // We will iterate through all client types later.
+    auto it = std::find(clients_.begin(), clients_.end(), last_shown_type_);
+    if (it == clients_.end()) {
+      DCHECK_EQ(last_shown_type_, SchedulerClientType::kUnknown);
+      last_shown_type_ = clients_.back();
+      it = clients_.end() - 1;
+    }
+
+    DCHECK_NE(last_shown_type_, SchedulerClientType::kUnknown);
+    size_t steps = 0u;
+
+    // Circling around all clients to find new notification to show.
+    // TODO(xingliu): Apply scheduling parameters here.
+    do {
+      // Move the iterator to next client type.
+      DCHECK(it != clients_.end());
+      if (++it == clients_.end())
+        it = clients_.begin();
+      ++steps;
+
+      SchedulerClientType type = *it;
+
+      // Check quota for all types and current background task type.
+      if (ReachDailyQuota())
+        break;
+
+      // Check quota for this type, and continue to iterate other types.
+      if (NoMoreNotificationToShow(type))
+        continue;
+
+      // Show the last notification in the vector. Notice the order depends on
+      // how the vector is sorted.
+      to_show->emplace(notifications_[type].back()->guid);
+      notifications_[type].pop_back();
+      shown_per_type_[type]++;
+      shown_++;
+      steps = 0u;
+
+      // Stop if we didn't find anything new to show, and have looped around
+      // all clients.
+    } while (steps <= clients_.size());
+  }
+
+  bool NoMoreNotificationToShow(SchedulerClientType type) {
+    auto it = client_states_.find(type);
+    int max_daily_show =
+        it == client_states_.end() ? 0 : it->second->current_max_daily_show;
+
+    return notifications_[type].empty() ||
+           shown_per_type_[type] >= config_->max_daily_shown_per_type ||
+           shown_per_type_[type] >= max_daily_show;
+  }
+
+  bool ReachDailyQuota() const {
+    return shown_ >= daily_max_to_show_all_types_;
+  }
+
+  // Scheduled notifications as candidates to display to the user.
+  Notifications notifications_;
+
+  const SchedulerTaskTime current_task_start_time_;
+  const ClientStates client_states_;
+  const SchedulerConfig* config_;
+  const std::vector<SchedulerClientType> clients_;
+  std::unique_ptr<DistributionPolicy> policy_;
+  int daily_max_to_show_all_types_;
+
+  SchedulerClientType last_shown_type_;
+  std::map<SchedulerClientType, int> shown_per_type_;
+  int shown_;
+
+  DISALLOW_COPY_AND_ASSIGN(DecisionHelper);
+};
+
+class DisplayDeciderImpl : public DisplayDecider {
+ public:
+  DisplayDeciderImpl() = default;
+  ~DisplayDeciderImpl() override = default;
+
+ private:
+  // DisplayDecider implementation.
+  void FindNotificationsToShow(
+      const SchedulerConfig* config,
+      std::vector<SchedulerClientType> clients,
+      std::unique_ptr<DistributionPolicy> distribution_policy,
+      SchedulerTaskTime task_start_time,
+      Notifications notifications,
+      ClientStates client_states,
+      Results* results) override {
+    auto helper = std::make_unique<DecisionHelper>(
+        config, std::move(clients), std::move(distribution_policy),
+        task_start_time, std::move(notifications), std::move(client_states));
+    helper->DecideNotificationToShow(results);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayDeciderImpl);
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<DisplayDecider> DisplayDecider::Create() {
+  return std::make_unique<DisplayDeciderImpl>();
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/display_decider.h b/chrome/browser/notifications/scheduler/internal/display_decider.h
new file mode 100644
index 0000000..cacb5df1
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/display_decider.h
@@ -0,0 +1,58 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_DISPLAY_DECIDER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_DISPLAY_DECIDER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace notifications {
+
+class DistributionPolicy;
+struct ClientState;
+struct NotificationEntry;
+struct SchedulerConfig;
+
+// This class uses scheduled notifications data and notification impression data
+// of each notification type to find a list of notification that should be
+// displayed to the user.
+// All operations should be done on the main thread.
+class DisplayDecider {
+ public:
+  using Notifications =
+      std::map<SchedulerClientType, std::vector<const NotificationEntry*>>;
+  using ClientStates = std::map<SchedulerClientType, const ClientState*>;
+  using Results = std::set<std::string>;
+
+  // Creates the decider to determine notifications to show.
+  static std::unique_ptr<DisplayDecider> Create();
+
+  DisplayDecider() = default;
+  virtual ~DisplayDecider() = default;
+
+  // Finds notifications to show. Returns a list of notification guids.
+  virtual void FindNotificationsToShow(
+      const SchedulerConfig* config,
+      std::vector<SchedulerClientType> clients,
+      std::unique_ptr<DistributionPolicy> distribution_policy,
+      SchedulerTaskTime task_start_time,
+      Notifications notifications,
+      ClientStates client_states,
+      Results* results) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DisplayDecider);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_DISPLAY_DECIDER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/display_decider_unittest.cc b/chrome/browser/notifications/scheduler/internal/display_decider_unittest.cc
new file mode 100644
index 0000000..da5bc05
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/display_decider_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2019 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/notifications/scheduler/internal/display_decider.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/notifications/scheduler/internal/distribution_policy.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+#include "chrome/browser/notifications/scheduler/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace notifications {
+namespace {
+
+// Default suppression info used in this test.
+const SuppressionInfo kSuppressionInfo =
+    SuppressionInfo(base::Time::Now(), base::TimeDelta::FromDays(56));
+
+// Initial state for test cases with a single registered client.
+const std::vector<test::ImpressionTestData> kSingleClientImpressionTestData = {
+    {SchedulerClientType::kTest1,
+     2 /* current_max_daily_show */,
+     {},
+     base::nullopt /* suppression_info */}};
+
+const std::vector<test::ImpressionTestData> kClientsImpressionTestData = {
+    {SchedulerClientType::kTest1,
+     2 /* current_max_daily_show */,
+     {},
+     base::nullopt /* suppression_info */},
+    {SchedulerClientType::kTest2,
+     5 /* current_max_daily_show */,
+     {},
+     base::nullopt /* suppression_info */},
+    {SchedulerClientType::kTest3,
+     0 /* current_max_daily_show */,
+     {},
+     kSuppressionInfo}};
+
+struct TestData {
+  // Impression data as the input.
+  std::vector<test::ImpressionTestData> impression_test_data;
+
+  // Notification entries as the input.
+  std::vector<NotificationEntry> notification_entries;
+
+  // The type of current background task.
+  SchedulerTaskTime task_start_time = SchedulerTaskTime::kUnknown;
+
+  // Expected output data.
+  DisplayDecider::Results expected;
+};
+
+std::string DebugString(const DisplayDecider::Results& results) {
+  std::string debug_string("notifications_to_show: \n");
+  for (const auto& guid : results)
+    debug_string += base::StringPrintf("%s ", guid.c_str());
+
+  return debug_string;
+}
+
+// TODO(xingliu): Add more test cases.
+class DisplayDeciderTest : public testing::Test {
+ public:
+  DisplayDeciderTest() = default;
+  ~DisplayDeciderTest() override = default;
+
+  void SetUp() override {
+    // Setup configuration used by this test.
+    config_.morning_task_hour = 7;
+    config_.evening_task_hour = 18;
+    config_.max_daily_shown_all_type = 3;
+  }
+
+ protected:
+  // Initializes a test case with input data.
+  void RunTestCase(const TestData& test_data) {
+    test_data_ = test_data;
+    test::AddImpressionTestData(test_data_.impression_test_data,
+                                &client_states_);
+
+    DisplayDecider::Notifications notifications;
+    for (const auto& entry : test_data_.notification_entries) {
+      notifications[entry.type].emplace_back(&entry);
+    }
+    std::vector<SchedulerClientType> clients;
+
+    std::map<SchedulerClientType, const ClientState*> client_states;
+    for (const auto& type : client_states_) {
+      client_states.emplace(type.first, type.second.get());
+      clients.emplace_back(type.first);
+    }
+
+    // Copy test inputs into |decider_|.
+    decider_ = DisplayDecider::Create();
+    decider_->FindNotificationsToShow(
+        &config_, std::move(clients), DistributionPolicy::Create(),
+        test_data_.task_start_time, std::move(notifications),
+        std::move(client_states), &results_);
+
+    // Verify output.
+    EXPECT_EQ(results_, test_data_.expected)
+        << "Actual result: \n"
+        << DebugString(results_) << " \n Expected result: \n"
+        << DebugString(test_data_.expected);
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  TestData test_data_;
+  SchedulerConfig config_;
+
+  std::map<SchedulerClientType, std::unique_ptr<ClientState>> client_states_;
+
+  // Test target class and output.
+  std::unique_ptr<DisplayDecider> decider_;
+  DisplayDecider::Results results_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayDeciderTest);
+};
+
+TEST_F(DisplayDeciderTest, NoNotification) {
+  TestData data{kClientsImpressionTestData,
+                {},
+                SchedulerTaskTime::kEvening,
+                DisplayDecider::Results()};
+  RunTestCase(data);
+}
+
+// Simple test case to verify new notifcaiton can be selected to show.
+TEST_F(DisplayDeciderTest, PickNewMorning) {
+  NotificationEntry entry(SchedulerClientType::kTest1, "guid123");
+  DisplayDecider::Results expected = {"guid123"};
+  TestData data{kSingleClientImpressionTestData,
+                {entry},
+                SchedulerTaskTime::kMorning,
+                std::move(expected)};
+  RunTestCase(data);
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/distribution_policy.cc b/chrome/browser/notifications/scheduler/internal/distribution_policy.cc
new file mode 100644
index 0000000..7319635
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/distribution_policy.cc
@@ -0,0 +1,46 @@
+// Copyright 2019 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/notifications/scheduler/internal/distribution_policy.h"
+
+#include "base/logging.h"
+
+namespace notifications {
+namespace {
+
+// Evenly distributes notifications to show in morning and evening. Morning
+// will have one more to show if the total quota is odd.
+class EvenDistributionHigherMorning : public DistributionPolicy {
+ public:
+  EvenDistributionHigherMorning() = default;
+  ~EvenDistributionHigherMorning() override = default;
+
+ private:
+  // DistributionPolicy implementation.
+  int MaxToShow(SchedulerTaskTime task_start_time, int quota) override {
+    DCHECK_GE(quota, 0);
+    switch (task_start_time) {
+      case SchedulerTaskTime::kUnknown:
+        NOTREACHED();
+        return quota;
+      case SchedulerTaskTime::kMorning:
+        return quota / 2 + quota % 2;
+      case SchedulerTaskTime::kEvening:
+        // The task running in the evening should flush all the remaining
+        // notifications.
+        return quota;
+    }
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(EvenDistributionHigherMorning);
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<DistributionPolicy> DistributionPolicy::Create() {
+  return std::make_unique<EvenDistributionHigherMorning>();
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/distribution_policy.h b/chrome/browser/notifications/scheduler/internal/distribution_policy.h
new file mode 100644
index 0000000..ac1a38bf
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/distribution_policy.h
@@ -0,0 +1,36 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_DISTRIBUTION_POLICY_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_DISTRIBUTION_POLICY_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace notifications {
+
+// Defines how to distribute notifications to show in different background tasks
+// in a day.
+class DistributionPolicy {
+ public:
+  // Creates the default distribution policy.
+  static std::unique_ptr<DistributionPolicy> Create();
+
+  DistributionPolicy() = default;
+  virtual ~DistributionPolicy() = default;
+
+  // Returns the maximum number of notifications to show in the background task
+  // starts at |task_start_time|. Suppose we at most can show |quota| number of
+  // new notifications during the current background task.
+  virtual int MaxToShow(SchedulerTaskTime task_start_time, int quota) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DistributionPolicy);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_DISTRIBUTION_POLICY_H_
diff --git a/chrome/browser/notifications/scheduler/internal/distribution_policy_unittest.cc b/chrome/browser/notifications/scheduler/internal/distribution_policy_unittest.cc
new file mode 100644
index 0000000..0606e61b
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/distribution_policy_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2019 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/notifications/scheduler/internal/distribution_policy.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace notifications {
+namespace {
+
+int MaxToShow(DistributionPolicy* policy,
+              SchedulerTaskTime task_start_time,
+              int quota) {
+  DCHECK(policy);
+  return policy->MaxToShow(task_start_time, quota);
+}
+
+TEST(DistributionPolicyTest, EvenDistributionHigherMorning) {
+  auto policy = DistributionPolicy::Create();
+
+  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 5 /* quota */),
+            3);
+  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 4 /* quota */),
+            2);
+
+  // Evening task should flush all remaining quota.
+  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kEvening, 5 /* quota */),
+            5);
+  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kEvening, 4 /* quota */),
+            4);
+
+  // Test 0 quota.
+  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 0 /* quota */),
+            0);
+  EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 0 /* quota */),
+            0);
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/icon_entry.cc b/chrome/browser/notifications/scheduler/internal/icon_entry.cc
new file mode 100644
index 0000000..d21f17c1
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/icon_entry.cc
@@ -0,0 +1,16 @@
+// Copyright 2019 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/notifications/scheduler/internal/icon_entry.h"
+
+namespace notifications {
+
+IconEntry::IconEntry() = default;
+
+IconEntry::IconEntry(IconEntry&& other) {
+  uuid.swap(other.uuid);
+  data.swap(other.data);
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/icon_entry.h b/chrome/browser/notifications/scheduler/internal/icon_entry.h
new file mode 100644
index 0000000..c941f04
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/icon_entry.h
@@ -0,0 +1,38 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_ENTRY_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_ENTRY_H_
+
+#include <string>
+#include <utility>
+
+#include "base/macros.h"
+
+namespace notifications {
+
+// The database entry that contains a notification icon, deserialized from the
+// icon protobuffer.
+// The icon can be a large chunk of memory so should be used in caution. The
+// format of the data is the same as the format in the protobuffer, and may need
+// to be converted to bitmap when used by the UI.
+struct IconEntry {
+  using IconData = std::string;
+
+  IconEntry();
+  IconEntry(IconEntry&& other);
+
+  // Unique identifier for the icon database entry.
+  std::string uuid;
+
+  // Raw data of the icon.
+  IconData data;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IconEntry);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/internal/icon_store.cc b/chrome/browser/notifications/scheduler/internal/icon_store.cc
new file mode 100644
index 0000000..9652056a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/icon_store.cc
@@ -0,0 +1,89 @@
+// Copyright 2019 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/notifications/scheduler/internal/icon_store.h"
+
+#include <utility>
+
+#include "chrome/browser/notifications/scheduler/internal/icon_entry.h"
+#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h"
+
+namespace leveldb_proto {
+
+void DataToProto(notifications::IconEntry* icon_entry,
+                 notifications::proto::Icon* proto) {
+  IconEntryToProto(icon_entry, proto);
+}
+
+void ProtoToData(notifications::proto::Icon* proto,
+                 notifications::IconEntry* icon_entry) {
+  IconEntryFromProto(proto, icon_entry);
+}
+
+}  // namespace leveldb_proto
+
+namespace notifications {
+
+using KeyEntryPair = std::pair<std::string, IconEntry>;
+using KeyEntryVector = std::vector<KeyEntryPair>;
+using KeyVector = std::vector<std::string>;
+
+IconProtoDbStore::IconProtoDbStore(
+    std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db)
+    : db_(std::move(db)), weak_ptr_factory_(this) {}
+
+IconProtoDbStore::~IconProtoDbStore() = default;
+
+void IconProtoDbStore::Init(InitCallback callback) {
+  db_->Init(base::BindOnce(&IconProtoDbStore::OnDbInitialized,
+                           weak_ptr_factory_.GetWeakPtr(),
+                           std::move(callback)));
+}
+
+void IconProtoDbStore::Load(const std::string& key, LoadCallback callback) {
+  db_->GetEntry(
+      key, base::BindOnce(&IconProtoDbStore::OnIconEntryLoaded,
+                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void IconProtoDbStore::Add(const std::string& key,
+                           std::unique_ptr<IconEntry> entry,
+                           UpdateCallback callback) {
+  auto entries_to_save = std::make_unique<KeyEntryVector>();
+  // TODO(xingliu): See if proto database can use
+  // std::vector<std::unique_ptr<T>> to avoid copy T into std::pair. Currently
+  // some code uses the copy constructor that force T to be copyable.
+  entries_to_save->emplace_back(
+      std::make_pair(key, std::move(*entry.release())));
+  db_->UpdateEntries(std::move(entries_to_save), std::make_unique<KeyVector>(),
+                     std::move(callback));
+}
+
+void IconProtoDbStore::Delete(const std::string& key, UpdateCallback callback) {
+  auto keys_to_delete = std::make_unique<KeyVector>();
+  keys_to_delete->emplace_back(key);
+  db_->UpdateEntries(std::make_unique<KeyEntryVector>(),
+                     std::move(keys_to_delete), std::move(callback));
+}
+
+void IconProtoDbStore::OnDbInitialized(
+    InitCallback callback,
+    leveldb_proto::Enums::InitStatus status) {
+  bool success = (status == leveldb_proto::Enums::InitStatus::kOK);
+  std::move(callback).Run(success);
+}
+
+void IconProtoDbStore::OnIconEntryLoaded(
+    LoadCallback callback,
+    bool success,
+    std::unique_ptr<IconEntry> icon_entry) {
+  if (!success) {
+    std::move(callback).Run(false, nullptr);
+    return;
+  }
+
+  std::move(callback).Run(true, std::move(icon_entry));
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/icon_store.h b/chrome/browser/notifications/scheduler/internal/icon_store.h
new file mode 100644
index 0000000..b016b79
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/icon_store.h
@@ -0,0 +1,95 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_STORE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_STORE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/notifications/proto/icon.pb.h"
+#include "chrome/browser/notifications/scheduler/internal/icon_entry.h"
+#include "components/leveldb_proto/public/proto_database.h"
+
+// Forward declaration for proto conversion.
+namespace leveldb_proto {
+void DataToProto(notifications::IconEntry* icon_entry,
+                 notifications::proto::Icon* proto);
+
+void ProtoToData(notifications::proto::Icon* proto,
+                 notifications::IconEntry* icon_entry);
+}  // namespace leveldb_proto
+
+namespace notifications {
+
+// Storage interface used to read/write icon data, each time only one icon can
+// be loaded into memory.
+class IconStore {
+ public:
+  using LoadCallback =
+      base::OnceCallback<void(bool, std::unique_ptr<IconEntry>)>;
+  using InitCallback = base::OnceCallback<void(bool)>;
+  using UpdateCallback = base::OnceCallback<void(bool)>;
+
+  // Initializes the storage.
+  virtual void Init(InitCallback callback) = 0;
+
+  // Loads one icon.
+  virtual void Load(const std::string& key, LoadCallback callback) = 0;
+
+  // Adds one icon to storage.
+  virtual void Add(const std::string& key,
+                   std::unique_ptr<IconEntry> entry,
+                   UpdateCallback callback) = 0;
+
+  // Deletes an icon.
+  virtual void Delete(const std::string& key, UpdateCallback callback) = 0;
+
+  IconStore() = default;
+  virtual ~IconStore() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IconStore);
+};
+
+// IconStore implementation backed by a proto database.
+class IconProtoDbStore : public IconStore {
+ public:
+  explicit IconProtoDbStore(
+      std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db);
+  ~IconProtoDbStore() override;
+
+ private:
+  // IconStore implementation.
+  void Init(InitCallback callback) override;
+  void Load(const std::string& key, LoadCallback callback) override;
+  void Add(const std::string& key,
+           std::unique_ptr<IconEntry> entry,
+           UpdateCallback callback) override;
+  void Delete(const std::string& key, UpdateCallback callback) override;
+
+  // Called when the proto database is initialized.
+  void OnDbInitialized(InitCallback callback,
+                       leveldb_proto::Enums::InitStatus status);
+
+  // Called when the icon is retrieved from the database.
+  void OnIconEntryLoaded(LoadCallback callback,
+                         bool success,
+                         std::unique_ptr<IconEntry> icon_entry);
+
+  // The proto database instance that persists data.
+  std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db_;
+
+  base::WeakPtrFactory<IconProtoDbStore> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(IconProtoDbStore);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc b/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc
new file mode 100644
index 0000000..3bb4ef3c
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc
@@ -0,0 +1,170 @@
+// Copyright 2019 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/notifications/scheduler/internal/icon_store.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/notifications/proto/icon.pb.h"
+#include "chrome/browser/notifications/scheduler/internal/icon_entry.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace notifications {
+namespace {
+
+const char kEntryKey[] = "guid_key1";
+const char kEntryKey2[] = "guid_key2";
+const char kEntryId[] = "proto_id_1";
+const char kEntryId2[] = "proto_id_2";
+const char kEntryData[] = "data_1";
+const char kEntryData2[] = "data_2";
+
+class IconStoreTest : public testing::Test {
+ public:
+  IconStoreTest() : load_result_(false), db_(nullptr) {}
+  ~IconStoreTest() override = default;
+
+  void SetUp() override {
+    IconEntry entry;
+    entry.uuid = kEntryId;
+    entry.data = kEntryData;
+    proto::Icon proto;
+    leveldb_proto::DataToProto(&entry, &proto);
+    db_entries_.emplace(kEntryKey, proto);
+
+    auto db =
+        std::make_unique<leveldb_proto::test::FakeDB<proto::Icon, IconEntry>>(
+            &db_entries_);
+    db_ = db.get();
+    store_ = std::make_unique<IconProtoDbStore>(std::move(db));
+  }
+
+  void InitDb() {
+    store()->Init(base::DoNothing());
+    db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+  }
+
+  void Load(const std::string& key) {
+    store()->Load(key, base::BindOnce(&IconStoreTest::OnEntryLoaded,
+                                      base::Unretained(this)));
+  }
+
+  void OnEntryLoaded(bool success, std::unique_ptr<IconEntry> entry) {
+    loaded_entry_ = std::move(entry);
+    load_result_ = success;
+  }
+
+ protected:
+  IconStore* store() { return store_.get(); }
+  leveldb_proto::test::FakeDB<proto::Icon, IconEntry>* db() { return db_; }
+  bool load_result() const { return load_result_; }
+  IconEntry* loaded_entry() { return loaded_entry_.get(); }
+
+  void VerifyEntry(const std::string& uuid, const std::string& data) {
+    DCHECK(loaded_entry_);
+    EXPECT_EQ(loaded_entry_->uuid, uuid);
+    EXPECT_EQ(loaded_entry_->data, data);
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<IconStore> store_;
+  std::map<std::string, proto::Icon> db_entries_;
+  std::unique_ptr<IconEntry> loaded_entry_;
+  bool load_result_;
+  leveldb_proto::test::FakeDB<proto::Icon, IconEntry>* db_;
+
+  DISALLOW_COPY_AND_ASSIGN(IconStoreTest);
+};
+
+TEST_F(IconStoreTest, Init) {
+  store()->Init(base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(IconStoreTest, InitFailed) {
+  store()->Init(base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kCorrupt);
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(IconStoreTest, LoadOne) {
+  InitDb();
+  Load(kEntryKey);
+  db()->GetCallback(true);
+
+  // Verify data is loaded.
+  DCHECK(loaded_entry());
+  EXPECT_TRUE(load_result());
+  VerifyEntry(kEntryId, kEntryData);
+}
+
+TEST_F(IconStoreTest, LoadFailed) {
+  InitDb();
+  Load(kEntryKey);
+  db()->GetCallback(false);
+
+  // Verify load failure.
+  EXPECT_FALSE(loaded_entry());
+  EXPECT_FALSE(load_result());
+}
+
+TEST_F(IconStoreTest, Add) {
+  InitDb();
+
+  auto new_entry = std::make_unique<IconEntry>();
+  new_entry->uuid = kEntryId2;
+  new_entry->data = kEntryData;
+
+  store()->Add(kEntryKey2, std::move(new_entry), base::DoNothing());
+  db()->UpdateCallback(true);
+
+  // Verify the entry is added.
+  Load(kEntryKey2);
+  db()->GetCallback(true);
+  EXPECT_TRUE(load_result());
+  VerifyEntry(kEntryId2, kEntryData);
+}
+
+TEST_F(IconStoreTest, AddDuplicate) {
+  InitDb();
+
+  auto new_entry = std::make_unique<IconEntry>();
+  new_entry->uuid = kEntryId;
+  new_entry->data = kEntryData2;
+
+  store()->Add(kEntryKey, std::move(new_entry), base::DoNothing());
+  db()->UpdateCallback(true);
+
+  // Add a duplicate id is currently allowed, we just update the entry.
+  Load(kEntryKey);
+  db()->GetCallback(true);
+  EXPECT_TRUE(load_result());
+  VerifyEntry(kEntryId, kEntryData2);
+}
+
+TEST_F(IconStoreTest, Delete) {
+  InitDb();
+
+  // Delete the only entry.
+  store()->Delete(kEntryKey,
+                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->UpdateCallback(true);
+
+  // No entry can be loaded, move nullptr as result.
+  Load(kEntryKey);
+  db()->GetCallback(true);
+  EXPECT_FALSE(loaded_entry());
+  EXPECT_TRUE(load_result());
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
new file mode 100644
index 0000000..7846da9c
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
@@ -0,0 +1,392 @@
+// Copyright 2019 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/notifications/scheduler/internal/impression_history_tracker.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/numerics/ranges.h"
+
+namespace notifications {
+
+// Comparator used to sort notification entries based on creation time.
+bool CreateTimeCompare(const Impression& lhs, const Impression& rhs) {
+  return lhs.create_time < rhs.create_time;
+}
+
+std::string ToDatabaseKey(SchedulerClientType type) {
+  switch (type) {
+    case SchedulerClientType::kTest1:
+      return "Test1";
+    case SchedulerClientType::kTest2:
+      return "Test2";
+    case SchedulerClientType::kTest3:
+      return "Test3";
+    case SchedulerClientType::kUnknown:
+      NOTREACHED();
+      return std::string();
+  }
+}
+
+ImpressionHistoryTrackerImpl::ImpressionHistoryTrackerImpl(
+    const SchedulerConfig& config,
+    std::unique_ptr<CollectionStore<ClientState>> store)
+    : store_(std::move(store)),
+      config_(config),
+      initialized_(false),
+      delegate_(nullptr),
+      weak_ptr_factory_(this) {}
+
+ImpressionHistoryTrackerImpl::~ImpressionHistoryTrackerImpl() = default;
+
+void ImpressionHistoryTrackerImpl::Init(Delegate* delegate,
+                                        InitCallback callback) {
+  DCHECK(!delegate_ && delegate);
+  delegate_ = delegate;
+  store_->InitAndLoad(
+      base::BindOnce(&ImpressionHistoryTrackerImpl::OnStoreInitialized,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory() {
+  for (auto& client_state : client_states_)
+    AnalyzeImpressionHistory(client_state.second.get());
+  if (MaybeUpdateAllDb())
+    NotifyImpressionUpdate();
+}
+
+void ImpressionHistoryTrackerImpl::GetClientStates(
+    std::map<SchedulerClientType, const ClientState*>* client_states) const {
+  DCHECK(client_states);
+  client_states->clear();
+  for (const auto& pair : client_states_) {
+    client_states->emplace(pair.first, pair.second.get());
+  }
+}
+
+void ImpressionHistoryTrackerImpl::OnClick(const std::string& notification_id) {
+  OnClickInternal(notification_id, true /*update_db*/);
+}
+
+void ImpressionHistoryTrackerImpl::OnActionClick(
+    const std::string& notification_id,
+    ActionButtonType button_type) {
+  OnButtonClickInternal(notification_id, button_type, true /*update_db*/);
+}
+
+void ImpressionHistoryTrackerImpl::OnDismiss(
+    const std::string& notification_id) {
+  OnDismissInternal(notification_id, true /*update_db*/);
+}
+
+void ImpressionHistoryTrackerImpl::OnStoreInitialized(
+    InitCallback callback,
+    bool success,
+    CollectionStore<ClientState>::Entries entries) {
+  if (!success) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  initialized_ = true;
+
+  // Load the data to memory, and sort the impression list.
+  // TODO(xingliu): Persist ClientState for new registered client and remove
+  // deprecated client. https://crbug.com/968606.
+  for (auto it = entries.begin(); it != entries.end(); ++it) {
+    auto& entry = (*it);
+    auto type = entry->type;
+    std::sort(entry->impressions.begin(), entry->impressions.end(),
+              &CreateTimeCompare);
+    for (auto& impression : entry->impressions) {
+      impression_map_.emplace(impression.guid, &impression);
+    }
+    client_states_.emplace(type, std::move(*it));
+  }
+
+  std::move(callback).Run(true);
+}
+
+void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory(
+    ClientState* client_state) {
+  DCHECK(client_state);
+  std::deque<Impression*> dismisses;
+  base::Time now = base::Time::Now();
+
+  for (auto it = client_state->impressions.begin();
+       it != client_state->impressions.end();) {
+    auto* impression = &*it;
+
+    // Prune out expired impression.
+    if (now - impression->create_time > config_.impression_expiration) {
+      impression_map_.erase(impression->guid);
+      client_state->impressions.erase(it++);
+      SetNeedsUpdate(client_state->type, true);
+      continue;
+    } else {
+      ++it;
+    }
+
+    // TODO(xingliu): Use scheduling params to determine ImpressionResult.
+    // https://crbug.com/968880
+    switch (impression->feedback) {
+      case UserFeedback::kDismiss:
+        dismisses.emplace_back(impression);
+        PruneImpressionByCreateTime(
+            &dismisses, impression->create_time - config_.dismiss_duration);
+
+        // Three consecutive dismisses will result in suppression.
+        ApplyNegativeImpressions(client_state, &dismisses,
+                                 config_.dismiss_count);
+        break;
+      case UserFeedback::kClick:
+        OnClickInternal(impression->guid, false /*update_db*/);
+        break;
+      case UserFeedback::kHelpful:
+        OnButtonClickInternal(impression->guid, ActionButtonType::kHelpful,
+                              false /*update_db*/);
+        break;
+      case UserFeedback::kNotHelpful:
+        OnButtonClickInternal(impression->guid, ActionButtonType::kUnhelpful,
+                              false /*update_db*/);
+        break;
+      case UserFeedback::kIgnore:
+        break;
+      case UserFeedback::kNoFeedback:
+        FALLTHROUGH;
+      default:
+        // The user didn't interact with the notification yet.
+        continue;
+        break;
+    }
+  }
+
+  // Check suppression expiration.
+  CheckSuppressionExpiration(client_state);
+}
+
+// static
+void ImpressionHistoryTrackerImpl::PruneImpressionByCreateTime(
+    std::deque<Impression*>* impressions,
+    const base::Time& start_time) {
+  DCHECK(impressions);
+  while (!impressions->empty()) {
+    if (impressions->front()->create_time > start_time)
+      break;
+    // Anything created before |start_time| is considered to have no effect and
+    // will never be processed again.
+    impressions->front()->integrated = true;
+    impressions->pop_front();
+  }
+}
+
+void ImpressionHistoryTrackerImpl::ApplyPositiveImpression(
+    ClientState* client_state,
+    Impression* impression) {
+  DCHECK(impression);
+  if (impression->integrated)
+    return;
+
+  SetNeedsUpdate(client_state->type, true);
+  impression->integrated = true;
+  impression->impression = ImpressionResult::kPositive;
+
+  // A positive impression directly releases the suppression.
+  if (client_state->suppression_info.has_value()) {
+    client_state->current_max_daily_show =
+        client_state->suppression_info->recover_goal;
+    client_state->suppression_info.reset();
+    return;
+  }
+
+  // Increase |current_max_daily_show| by 1.
+  client_state->current_max_daily_show =
+      base::ClampToRange(++client_state->current_max_daily_show, 0,
+                         config_.max_daily_shown_per_type);
+}
+
+void ImpressionHistoryTrackerImpl::ApplyNegativeImpressions(
+    ClientState* client_state,
+    std::deque<Impression*>* impressions,
+    size_t num_actions) {
+  if (impressions->size() < num_actions)
+    return;
+
+  // Suppress the notification if the user performed consecutive operations that
+  // generates negative impressions.
+  for (size_t i = 0, size = impressions->size(); i < size; ++i) {
+    if ((*impressions)[i]->integrated)
+      continue;
+
+    (*impressions)[i]->integrated = true;
+
+    // Each user feedback after |num_action| will apply a new negative
+    // impression.
+    if (i + 1 >= num_actions)
+      ApplyNegativeImpression(client_state, (*impressions)[i]);
+  }
+}
+
+void ImpressionHistoryTrackerImpl::ApplyNegativeImpression(
+    ClientState* client_state,
+    Impression* impression) {
+  if (impression->integrated)
+    return;
+
+  SetNeedsUpdate(client_state->type, true);
+  impression->integrated = true;
+  impression->impression = ImpressionResult::kNegative;
+
+  // Suppress the notification, the user will not see this type of notification
+  // for a while.
+  SuppressionInfo supression_info(base::Time::Now(),
+                                  config_.suppression_duration);
+  client_state->suppression_info = std::move(supression_info);
+  client_state->current_max_daily_show = 0;
+}
+
+void ImpressionHistoryTrackerImpl::CheckSuppressionExpiration(
+    ClientState* client_state) {
+  // No suppression to recover from.
+  if (!client_state->suppression_info.has_value())
+    return;
+
+  SuppressionInfo& suppression = client_state->suppression_info.value();
+  base::Time now = base::Time::Now();
+
+  // Still in the suppression time window.
+  if (now - suppression.last_trigger_time < suppression.duration)
+    return;
+
+  // Recover from suppression and increase |current_max_daily_show|.
+  DCHECK_EQ(client_state->current_max_daily_show, 0);
+  client_state->current_max_daily_show = suppression.recover_goal;
+
+  // Clear suppression if fully recovered.
+  client_state->suppression_info.reset();
+  SetNeedsUpdate(client_state->type, true);
+}
+
+bool ImpressionHistoryTrackerImpl::MaybeUpdateDb(SchedulerClientType type) {
+  auto it = client_states_.find(type);
+  if (it == client_states_.end())
+    return false;
+
+  bool db_updated = false;
+  if (NeedsUpdate(type)) {
+    store_->Update(ToDatabaseKey(type), *(it->second.get()), base::DoNothing());
+    db_updated = true;
+  }
+  SetNeedsUpdate(type, false);
+  return db_updated;
+}
+
+bool ImpressionHistoryTrackerImpl::MaybeUpdateAllDb() {
+  bool db_updated = false;
+  for (const auto& client_state : client_states_) {
+    auto type = client_state.second->type;
+    db_updated |= MaybeUpdateDb(type);
+  }
+
+  return db_updated;
+}
+
+void ImpressionHistoryTrackerImpl::SetNeedsUpdate(SchedulerClientType type,
+                                                  bool needs_update) {
+  need_update_db_[type] = needs_update;
+}
+
+bool ImpressionHistoryTrackerImpl::NeedsUpdate(SchedulerClientType type) const {
+  auto it = need_update_db_.find(type);
+  return it == need_update_db_.end() ? false : it->second;
+}
+
+void ImpressionHistoryTrackerImpl::NotifyImpressionUpdate() {
+  if (delegate_)
+    delegate_->OnImpressionUpdated();
+}
+
+Impression* ImpressionHistoryTrackerImpl::FindImpressionNeedsUpdate(
+    const std::string& notification_guid) {
+  auto it = impression_map_.find(notification_guid);
+  if (it == impression_map_.end())
+    return nullptr;
+  auto* impression = it->second;
+
+  if (impression->integrated)
+    return nullptr;
+
+  return it->second;
+}
+
+void ImpressionHistoryTrackerImpl::OnClickInternal(
+    const std::string& notification_guid,
+    bool update_db) {
+  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  if (!impression)
+    return;
+
+  auto it = client_states_.find(impression->type);
+  if (it == client_states_.end())
+    return;
+  ClientState* client_state = it->second.get();
+  ApplyPositiveImpression(client_state, impression);
+  impression->feedback = UserFeedback::kClick;
+
+  if (update_db && MaybeUpdateDb(client_state->type))
+    NotifyImpressionUpdate();
+}
+
+void ImpressionHistoryTrackerImpl::OnButtonClickInternal(
+    const std::string& notification_guid,
+    ActionButtonType button_type,
+    bool update_db) {
+  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  if (!impression)
+    return;
+  auto it = client_states_.find(impression->type);
+  if (it == client_states_.end())
+    return;
+
+  ClientState* client_state = it->second.get();
+  switch (button_type) {
+    case ActionButtonType::kHelpful:
+      ApplyPositiveImpression(client_state, impression);
+      impression->feedback = UserFeedback::kHelpful;
+      break;
+    case ActionButtonType::kUnhelpful:
+      ApplyNegativeImpression(client_state, impression);
+      impression->feedback = UserFeedback::kNotHelpful;
+      break;
+    case ActionButtonType::kUnknownAction:
+      NOTIMPLEMENTED();
+      break;
+  }
+
+  if (update_db && MaybeUpdateDb(client_state->type))
+    NotifyImpressionUpdate();
+}
+
+void ImpressionHistoryTrackerImpl::OnDismissInternal(
+    const std::string& notification_guid,
+    bool update_db) {
+  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  if (!impression)
+    return;
+
+  auto it = client_states_.find(impression->type);
+  if (it == client_states_.end())
+    return;
+  ClientState* client_state = it->second.get();
+
+  AnalyzeImpressionHistory(client_state);
+
+  if (update_db && MaybeUpdateDb(client_state->type))
+    NotifyImpressionUpdate();
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
new file mode 100644
index 0000000..d3e41f3
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
@@ -0,0 +1,163 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_HISTORY_TRACKER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_HISTORY_TRACKER_H_
+
+#include <deque>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/notifications/scheduler/internal/collection_store.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_types.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/public/user_action_handler.h"
+
+namespace notifications {
+
+// Provides functionalities to update notification impression history and adjust
+// maximum daily notification shown to the user.
+class ImpressionHistoryTracker : public UserActionHandler {
+ public:
+  using ClientStates =
+      std::map<SchedulerClientType, std::unique_ptr<ClientState>>;
+  using InitCallback = base::OnceCallback<void(bool)>;
+
+  class Delegate {
+   public:
+    Delegate() = default;
+    virtual ~Delegate() = default;
+
+    // Called when the impression data is updated.
+    virtual void OnImpressionUpdated() = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Delegate);
+  };
+
+  // Initializes the impression tracker.
+  virtual void Init(Delegate* delegate, InitCallback callback) = 0;
+
+  // Analyzes the impression history for all notification clients, and adjusts
+  // the |current_max_daily_show|.
+  virtual void AnalyzeImpressionHistory() = 0;
+
+  // Queries the client states.
+  virtual void GetClientStates(
+      std::map<SchedulerClientType, const ClientState*>* client_states)
+      const = 0;
+
+  virtual ~ImpressionHistoryTracker() = default;
+
+ protected:
+  ImpressionHistoryTracker() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTracker);
+};
+
+// An implementation of ImpressionHistoryTracker backed by a database.
+class ImpressionHistoryTrackerImpl : public ImpressionHistoryTracker {
+ public:
+  explicit ImpressionHistoryTrackerImpl(
+      const SchedulerConfig& config,
+      std::unique_ptr<CollectionStore<ClientState>> store);
+  ~ImpressionHistoryTrackerImpl() override;
+
+ private:
+  // ImpressionHistoryTracker implementation.
+  void Init(Delegate* delegate, InitCallback callback) override;
+  void AnalyzeImpressionHistory() override;
+  void GetClientStates(std::map<SchedulerClientType, const ClientState*>*
+                           client_states) const override;
+  void OnClick(const std::string& notification_id) override;
+  void OnActionClick(const std::string& notification_id,
+                     ActionButtonType button_type) override;
+  void OnDismiss(const std::string& notification_id) override;
+
+  // Called after |store_| is initialized.
+  void OnStoreInitialized(InitCallback callback,
+                          bool success,
+                          CollectionStore<ClientState>::Entries entries);
+
+  // Helper method to prune impressions created before |start_time|. Assumes
+  // |impressions| are sorted by creation time.
+  static void PruneImpressionByCreateTime(std::deque<Impression*>* impressions,
+                                          const base::Time& start_time);
+
+  // Analyzes the impression history for a particular client.
+  void AnalyzeImpressionHistory(ClientState* client_state);
+
+  // Applies a positive impression result to this notification type.
+  void ApplyPositiveImpression(ClientState* client_state,
+                               Impression* impression);
+
+  // Applies negative impression on this notification type when |num_actions|
+  // consecutive negative impression result are generated.
+  void ApplyNegativeImpressions(ClientState* client_state,
+                                std::deque<Impression*>* impressions,
+                                size_t num_actions);
+
+  // Applies one negative impression.
+  void ApplyNegativeImpression(ClientState* client_state,
+                               Impression* impression);
+
+  // Checks if suppression is expired and recover to a certain daily quota.
+  void CheckSuppressionExpiration(ClientState* client_state);
+
+  // Tries to update the database records for |type|. Returns whether the db is
+  // actually updated.
+  bool MaybeUpdateDb(SchedulerClientType type);
+  bool MaybeUpdateAllDb();
+
+  // Sets/Gets the flag if impression data for |type| needs update in the
+  // database.
+  void SetNeedsUpdate(SchedulerClientType type, bool needs_update);
+  bool NeedsUpdate(SchedulerClientType type) const;
+
+  // Notifies the delegate about impression data update.
+  void NotifyImpressionUpdate();
+
+  // Finds an impression that needs to update based on notification id.
+  Impression* FindImpressionNeedsUpdate(const std::string& notification_guid);
+
+  void OnClickInternal(const std::string& notification_guid, bool update_db);
+  void OnButtonClickInternal(const std::string& notification_guid,
+                             ActionButtonType button_type,
+                             bool update_db);
+  void OnDismissInternal(const std::string& notification_guid, bool update_db);
+
+  // Impression history and global states for all notification scheduler
+  // clients.
+  ClientStates client_states_;
+
+  // Notification guid to Impression map.
+  std::map<std::string, Impression*> impression_map_;
+
+  // The storage that persists data.
+  std::unique_ptr<CollectionStore<ClientState>> store_;
+
+  // System configuration.
+  const SchedulerConfig& config_;
+
+  // Whether the impression tracker is successfully initialized.
+  bool initialized_;
+
+  // If the database needs an update when any of the impression data is updated.
+  std::map<SchedulerClientType, bool> need_update_db_;
+
+  Delegate* delegate_;
+
+  base::WeakPtrFactory<ImpressionHistoryTrackerImpl> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerImpl);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_HISTORY_TRACKER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc
new file mode 100644
index 0000000..26a89e3
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc
@@ -0,0 +1,274 @@
+// Copyright 2019 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/bind.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h"
+#include "chrome/browser/notifications/scheduler/test/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using ::testing::Invoke;
+using StoreEntries = std::vector<std::unique_ptr<notifications::ClientState>>;
+
+namespace notifications {
+namespace {
+
+const char kGuid1[] = "guid1";
+
+struct TestCase {
+  // Input data that will be pushed to the target class.
+  std::vector<test::ImpressionTestData> input;
+
+  // Expected output data.
+  std::vector<test::ImpressionTestData> expected;
+};
+
+Impression CreateImpression(const base::Time& create_time,
+                            const std::string& guid) {
+  return {create_time,
+          UserFeedback::kNoFeedback,
+          ImpressionResult::kInvalid,
+          false /* integrated */,
+          SchedulerTaskTime::kMorning,
+          guid,
+          SchedulerClientType::kTest1};
+}
+
+TestCase CreateDefaultTestCase() {
+  TestCase test_case;
+  test_case.input = {{SchedulerClientType::kTest1,
+                      2 /* current_max_daily_show */,
+                      {},
+                      base::nullopt /* suppression_info */}};
+  test_case.expected = test_case.input;
+  return test_case;
+}
+
+class MockImpressionStore : public CollectionStore<ClientState> {
+ public:
+  MockImpressionStore() {}
+
+  MOCK_METHOD1(InitAndLoad, void(CollectionStore<ClientState>::LoadCallback));
+  MOCK_METHOD3(Add,
+               void(const std::string&,
+                    const ClientState&,
+                    base::OnceCallback<void(bool)>));
+  MOCK_METHOD3(Update,
+               void(const std::string&,
+                    const ClientState&,
+                    base::OnceCallback<void(bool)>));
+  MOCK_METHOD2(Delete,
+               void(const std::string&, base::OnceCallback<void(bool)>));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockImpressionStore);
+};
+
+class MockDelegate : public ImpressionHistoryTracker::Delegate {
+ public:
+  MockDelegate() = default;
+  ~MockDelegate() = default;
+  MOCK_METHOD0(OnImpressionUpdated, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDelegate);
+};
+
+// TODO(xingliu): Add more test cases following the test doc.
+class ImpressionHistoryTrackerTest : public ::testing::Test {
+ public:
+  ImpressionHistoryTrackerTest() : store_(nullptr), delegate_(nullptr) {}
+  ~ImpressionHistoryTrackerTest() override = default;
+
+  void SetUp() override {
+    config_.impression_expiration = base::TimeDelta::FromDays(28);
+    config_.suppression_duration = base::TimeDelta::FromDays(56);
+  }
+
+ protected:
+  // Creates the tracker and push in data.
+  void InitTrackerWithData(const TestCase& test_case) {
+    StoreEntries entries;
+    test::AddImpressionTestData(test_case.input, &entries);
+
+    auto store = std::make_unique<MockImpressionStore>();
+    store_ = store.get();
+    delegate_ = std::make_unique<MockDelegate>();
+    impression_trakcer_ = std::make_unique<ImpressionHistoryTrackerImpl>(
+        config_, std::move(store));
+
+    // Initialize the store and call the callback.
+    EXPECT_CALL(*store_, InitAndLoad(_))
+        .WillOnce(
+            Invoke([&entries](base::OnceCallback<void(bool, StoreEntries)> cb) {
+              std::move(cb).Run(true, std::move(entries));
+            }));
+    base::RunLoop loop;
+    impression_trakcer_->Init(
+        delegate_.get(), base::BindOnce(
+                             [](base::RepeatingClosure closure, bool success) {
+                               EXPECT_TRUE(success);
+                               std::move(closure).Run();
+                             },
+                             loop.QuitClosure()));
+    loop.Run();
+  }
+
+  // Verifies the |expected_test_data| matches the internal states.
+  void VerifyClientStates(const TestCase& test_case) {
+    std::map<SchedulerClientType, const ClientState*> client_states;
+    impression_trakcer_->GetClientStates(&client_states);
+
+    ImpressionHistoryTracker::ClientStates expected_client_states;
+    test::AddImpressionTestData(test_case.expected, &expected_client_states);
+
+    DCHECK_EQ(expected_client_states.size(), client_states.size());
+    for (const auto& expected : expected_client_states) {
+      auto output_it = client_states.find(expected.first);
+      DCHECK(output_it != client_states.end());
+      EXPECT_EQ(*expected.second, *output_it->second)
+          << "Unmatch client states: \n"
+          << "Expected: \n"
+          << expected.second->DebugPrint() << " \n"
+          << "Acutual: \n"
+          << output_it->second->DebugPrint();
+    }
+  }
+
+  const SchedulerConfig& config() const { return config_; }
+  MockImpressionStore* store() { return store_; }
+  MockDelegate* delegate() { return delegate_.get(); }
+  ImpressionHistoryTracker* tracker() { return impression_trakcer_.get(); }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  SchedulerConfig config_;
+  std::unique_ptr<ImpressionHistoryTracker> impression_trakcer_;
+  MockImpressionStore* store_;
+  std::unique_ptr<MockDelegate> delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerTest);
+};
+
+// Verifies expired impression will be deleted.
+TEST_F(ImpressionHistoryTrackerTest, DeleteExpiredImpression) {
+  TestCase test_case;
+  auto expired_create_time = base::Time::Now() - base::TimeDelta::FromDays(1) -
+                             config().impression_expiration;
+  auto not_expired_time = base::Time::Now() + base::TimeDelta::FromDays(1) -
+                          config().impression_expiration;
+  Impression expired{expired_create_time,         UserFeedback::kNoFeedback,
+                     ImpressionResult::kInvalid,  false /* integrated */,
+                     SchedulerTaskTime::kMorning, "guid1",
+                     SchedulerClientType::kTest1};
+  Impression not_expired{not_expired_time,
+                         UserFeedback::kNoFeedback,
+                         ImpressionResult::kInvalid,
+                         false /* integrated */,
+                         SchedulerTaskTime::kMorning,
+                         "guid2",
+                         SchedulerClientType::kTest1};
+
+  // The impressions in the input should be sorted by creation time when gets
+  // loaded to memory.
+  test_case.input = {{SchedulerClientType::kTest1,
+                      2 /* current_max_daily_show */,
+                      {expired, not_expired, expired},
+                      base::nullopt /* suppression_info */}};
+
+  // Expired impression created in |expired_create_time| should be deleted.
+  // No change expected on the next impression, which is not expired and no user
+  // feedback .
+  test_case.expected = {{SchedulerClientType::kTest1,
+                         2 /* current_max_daily_show */,
+                         {not_expired},
+                         base::nullopt /* suppression_info */}};
+
+  InitTrackerWithData(test_case);
+  EXPECT_CALL(*store(), Update(_, _, _));
+  EXPECT_CALL(*delegate(), OnImpressionUpdated());
+  tracker()->AnalyzeImpressionHistory();
+  VerifyClientStates(test_case);
+}
+
+// If impression has been deleted, click should have no result.
+TEST_F(ImpressionHistoryTrackerTest, ClickNoImpression) {
+  TestCase test_case = CreateDefaultTestCase();
+  InitTrackerWithData(test_case);
+  EXPECT_CALL(*store(), Update(_, _, _)).Times(0);
+  EXPECT_CALL(*delegate(), OnImpressionUpdated()).Times(0);
+  tracker()->OnClick(kGuid1);
+  VerifyClientStates(test_case);
+}
+
+struct UserActionTestParam {
+  ImpressionResult impression_result = ImpressionResult::kInvalid;
+  UserFeedback user_feedback = UserFeedback::kNoFeedback;
+  int current_max_daily_show = 0;
+  base::Optional<ActionButtonType> button_type;
+  base::Optional<SuppressionInfo> suppression_info;
+};
+
+class ImpressionHistoryTrackerUserActionTest
+    : public ImpressionHistoryTrackerTest,
+      public ::testing::WithParamInterface<UserActionTestParam> {
+ public:
+  ImpressionHistoryTrackerUserActionTest() = default;
+  ~ImpressionHistoryTrackerUserActionTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerUserActionTest);
+};
+
+// TODO(xingliu): Add test for unhelpful/dismiss, need to use base::Clock to
+// mock base::Time::Now().
+const UserActionTestParam kUserActionTestParams[] = {
+    {ImpressionResult::kPositive, UserFeedback::kClick, 3, base::nullopt,
+     base::nullopt},
+    {ImpressionResult::kPositive, UserFeedback::kHelpful, 3,
+     ActionButtonType::kHelpful, base::nullopt}};
+
+// User actions like clicks should update the ClientState data accordingly.
+TEST_P(ImpressionHistoryTrackerUserActionTest, UserAction) {
+  TestCase test_case = CreateDefaultTestCase();
+  Impression impression = CreateImpression(base::Time::Now(), kGuid1);
+  DCHECK(!test_case.input.empty());
+  test_case.input.front().impressions.emplace_back(impression);
+
+  impression.impression = GetParam().impression_result;
+  impression.integrated = true;
+  impression.feedback = GetParam().user_feedback;
+
+  test_case.expected.front().current_max_daily_show =
+      GetParam().current_max_daily_show;
+  test_case.expected.front().impressions.emplace_back(impression);
+  test_case.expected.front().suppression_info = GetParam().suppression_info;
+
+  InitTrackerWithData(test_case);
+  EXPECT_CALL(*store(), Update(_, _, _));
+  EXPECT_CALL(*delegate(), OnImpressionUpdated());
+
+  // Trigger user action.
+  if (GetParam().user_feedback == UserFeedback::kClick)
+    tracker()->OnClick(kGuid1);
+  else if (GetParam().button_type.has_value())
+    tracker()->OnActionClick(kGuid1, GetParam().button_type.value());
+
+  VerifyClientStates(test_case);
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         ImpressionHistoryTrackerUserActionTest,
+                         testing::ValuesIn(kUserActionTestParams));
+
+}  // namespace
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_store.cc b/chrome/browser/notifications/scheduler/internal/impression_store.cc
new file mode 100644
index 0000000..c917b3d5
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/impression_store.cc
@@ -0,0 +1,100 @@
+// Copyright 2019 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/notifications/scheduler/internal/impression_store.h"
+
+#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h"
+
+namespace leveldb_proto {
+
+void DataToProto(notifications::ClientState* client_state,
+                 notifications::proto::ClientState* proto) {
+  ClientStateToProto(client_state, proto);
+}
+
+void ProtoToData(notifications::proto::ClientState* proto,
+                 notifications::ClientState* client_state) {
+  ClientStateFromProto(proto, client_state);
+}
+
+}  // namespace leveldb_proto
+
+namespace notifications {
+
+ImpressionStore::ImpressionStore(
+    std::unique_ptr<
+        leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db)
+    : db_(std::move(db)), weak_ptr_factory_(this) {}
+
+ImpressionStore::~ImpressionStore() = default;
+
+void ImpressionStore::InitAndLoad(LoadCallback callback) {
+  db_->Init(base::BindOnce(&ImpressionStore::OnDbInitialized,
+                           weak_ptr_factory_.GetWeakPtr(),
+                           std::move(callback)));
+}
+
+void ImpressionStore::OnDbInitialized(LoadCallback callback,
+                                      leveldb_proto::Enums::InitStatus status) {
+  if (status != leveldb_proto::Enums::InitStatus::kOK) {
+    std::move(callback).Run(false, Entries());
+    return;
+  }
+
+  // Load the data after a successful initialization.
+  db_->LoadEntries(base::BindOnce(&ImpressionStore::OnDataLoaded,
+                                  weak_ptr_factory_.GetWeakPtr(),
+                                  std::move(callback)));
+}
+
+void ImpressionStore::OnDataLoaded(LoadCallback callback,
+                                   bool success,
+                                   std::unique_ptr<EntryVector> entry_vector) {
+  // The database failed to load.
+  if (!success) {
+    std::move(callback).Run(false, Entries());
+    return;
+  }
+
+  // Success to load but no data.
+  if (!entry_vector) {
+    std::move(callback).Run(true, Entries());
+    return;
+  }
+
+  // Load data.
+  Entries entries;
+  for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) {
+    std::unique_ptr<ClientState> client_state = std::make_unique<ClientState>();
+    *client_state = std::move(*it);
+    entries.emplace_back(std::move(client_state));
+  }
+
+  std::move(callback).Run(true, std::move(entries));
+}
+
+void ImpressionStore::Add(const std::string& key,
+                          const ClientState& client_state,
+                          UpdateCallback callback) {
+  Update(key, client_state, std::move(callback));
+}
+
+void ImpressionStore::Update(const std::string& key,
+                             const ClientState& client_state,
+                             UpdateCallback callback) {
+  auto entries_to_save = std::make_unique<KeyEntryVector>();
+  entries_to_save->emplace_back(std::make_pair(key, client_state));
+  db_->UpdateEntries(std::move(entries_to_save),
+                     std::make_unique<KeyVector>() /*keys_to_remove*/,
+                     std::move(callback));
+}
+
+void ImpressionStore::Delete(const std::string& key, UpdateCallback callback) {
+  auto keys_to_delete = std::make_unique<KeyVector>();
+  keys_to_delete->emplace_back(key);
+  db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/,
+                     std::move(keys_to_delete), std::move(callback));
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_store.h b/chrome/browser/notifications/scheduler/internal/impression_store.h
new file mode 100644
index 0000000..b7607d1
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/impression_store.h
@@ -0,0 +1,73 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_STORE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_STORE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/notifications/proto/client_state.pb.h"
+#include "chrome/browser/notifications/scheduler/internal/collection_store.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_types.h"
+#include "components/leveldb_proto/public/proto_database.h"
+
+// Forward declaration for proto conversion.
+namespace leveldb_proto {
+void DataToProto(notifications::ClientState* client_state,
+                 notifications::proto::ClientState* proto);
+
+void ProtoToData(notifications::proto::ClientState* proto,
+                 notifications::ClientState* client_state);
+}  // namespace leveldb_proto
+
+namespace notifications {
+
+// An impression storage using a proto database to persist data.
+class ImpressionStore : public CollectionStore<ClientState> {
+ public:
+  ImpressionStore(
+      std::unique_ptr<
+          leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db);
+  ~ImpressionStore() override;
+
+ private:
+  using KeyEntryVector = std::vector<std::pair<std::string, ClientState>>;
+  using KeyVector = std::vector<std::string>;
+  using EntryVector = std::vector<ClientState>;
+
+  // CollectionStore implementation.
+  void InitAndLoad(LoadCallback callback) override;
+  void Add(const std::string& key,
+           const ClientState& client_state,
+           UpdateCallback callback) override;
+  void Update(const std::string& key,
+              const ClientState& client_state,
+              UpdateCallback callback) override;
+  void Delete(const std::string& key, UpdateCallback callback) override;
+
+  // Called when the proto database is initialized but no yet loading the data
+  // into memory.
+  void OnDbInitialized(LoadCallback callback,
+                       leveldb_proto::Enums::InitStatus status);
+
+  // Called after loading the data from database.
+  void OnDataLoaded(LoadCallback callback,
+                    bool success,
+                    std::unique_ptr<EntryVector> entry_vector);
+
+  std::unique_ptr<leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>>
+      db_;
+  base::WeakPtrFactory<ImpressionStore> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImpressionStore);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/internal/impression_store_unittest.cc b/chrome/browser/notifications/scheduler/internal/impression_store_unittest.cc
new file mode 100644
index 0000000..edc2dd27
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/impression_store_unittest.cc
@@ -0,0 +1,207 @@
+// Copyright 2019 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/notifications/scheduler/internal/impression_store.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/notifications/proto/client_state.pb.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_types.h"
+#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using leveldb_proto::test::FakeDB;
+using InitStatus = leveldb_proto::Enums::InitStatus;
+using Entries = notifications::ImpressionStore::Entries;
+using DbEntries = std::vector<notifications::ClientState>;
+using DbEntriesPtr = std::unique_ptr<std::vector<notifications::ClientState>>;
+using TestClientStates = std::map<std::string, notifications::ClientState>;
+
+namespace notifications {
+namespace {
+
+const char kClientStateKey[] = "guid_client_state_key1";
+const ClientState kDefaultClientState;
+
+// Test fixture to verify impression store.
+class ImpressionStoreTest : public testing::Test {
+ public:
+  ImpressionStoreTest() : load_result_(false), db_(nullptr) {}
+  ~ImpressionStoreTest() override = default;
+
+  void SetUp() override {}
+
+ protected:
+  // Initialize the store with test data.
+  void Init(const TestClientStates& test_data, InitStatus status) {
+    CreateTestProto(test_data);
+
+    auto db =
+        std::make_unique<FakeDB<proto::ClientState, ClientState>>(&db_entries_);
+    db_ = db.get();
+    store_ = std::make_unique<ImpressionStore>(std::move(db));
+    store_->InitAndLoad(base::BindOnce(&ImpressionStoreTest::OnEntriesLoaded,
+                                       base::Unretained(this)));
+    db_->InitStatusCallback(status);
+  }
+
+  bool load_result() const { return load_result_; }
+  const Entries& loaded_entries() const { return loaded_entries_; }
+  FakeDB<proto::ClientState, ClientState>* db() { return db_; }
+
+  CollectionStore<ClientState>* store() { return store_.get(); }
+
+  void OnEntriesLoaded(bool success, Entries entries) {
+    loaded_entries_ = std::move(entries);
+    load_result_ = success;
+  }
+
+  // Verifies the entries in the db is |expected|.
+  void VerifyDataInDb(DbEntriesPtr expected) {
+    db_->LoadEntries(base::BindOnce(
+        [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) {
+          EXPECT_TRUE(success);
+          DCHECK(entries);
+          DCHECK(expected);
+          EXPECT_EQ(entries->size(), expected->size());
+          for (size_t i = 0, size = entries->size(); i < size; ++i) {
+            EXPECT_EQ((*entries)[i], (*expected)[i]);
+          }
+        },
+        std::move(expected)));
+    db_->LoadCallback(true);
+  }
+
+ private:
+  void CreateTestProto(const TestClientStates& client_states) {
+    for (const auto& pair : client_states) {
+      auto client_state(pair.second);
+      auto key = pair.first;
+      notifications::proto::ClientState proto;
+      ClientStateToProto(&client_state, &proto);
+      db_entries_.emplace(key, proto);
+    }
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::map<std::string, proto::ClientState> db_entries_;
+  bool load_result_;
+  Entries loaded_entries_;
+
+  FakeDB<proto::ClientState, ClientState>* db_;
+  std::unique_ptr<CollectionStore<ClientState>> store_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImpressionStoreTest);
+};
+
+// Initializes an empty database should success.
+TEST_F(ImpressionStoreTest, InitSuccessEmptyDb) {
+  Init(TestClientStates(), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(loaded_entries().empty());
+}
+
+// Initialize non-empty database should success.
+TEST_F(ImpressionStoreTest, InitSuccessWithData) {
+  auto test_data = TestClientStates();
+  test_data.emplace(kClientStateKey, kDefaultClientState);
+  Init(test_data, InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_EQ(loaded_entries().size(), 1u);
+  EXPECT_EQ(*loaded_entries().back(), kDefaultClientState);
+}
+
+// Failure when loading the data will result in error.
+TEST_F(ImpressionStoreTest, InitSuccessLoadFailed) {
+  Init(TestClientStates(), InitStatus::kOK);
+  db()->LoadCallback(false);
+  EXPECT_EQ(load_result(), false);
+  EXPECT_TRUE(loaded_entries().empty());
+}
+
+// Failed database initialization will result in error.
+TEST_F(ImpressionStoreTest, InitFailed) {
+  Init(TestClientStates(), InitStatus::kCorrupt);
+  EXPECT_EQ(load_result(), false);
+  EXPECT_TRUE(loaded_entries().empty());
+}
+
+// Verifies adding data.
+TEST_F(ImpressionStoreTest, Add) {
+  Init(TestClientStates(), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(loaded_entries().empty());
+
+  // Add data to the store.
+  store()->Add(kClientStateKey, kDefaultClientState,
+               base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->UpdateCallback(true);
+
+  // Verify the new data is in the database.
+  auto expected = std::make_unique<DbEntries>();
+  expected->emplace_back(kDefaultClientState);
+  VerifyDataInDb(std::move(expected));
+}
+
+// Verifies failure when adding data will result in error.
+TEST_F(ImpressionStoreTest, AddFailed) {
+  Init(TestClientStates(), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(loaded_entries().empty());
+
+  store()->Add(kClientStateKey, kDefaultClientState,
+               base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
+  db()->UpdateCallback(false);
+}
+
+// Verifies updating data.
+TEST_F(ImpressionStoreTest, Update) {
+  auto test_data = TestClientStates();
+  test_data.emplace(kClientStateKey, kDefaultClientState);
+  Init(test_data, InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+
+  ClientState new_client_state;
+  new_client_state.current_max_daily_show = 100;
+
+  // Update the database.
+  store()->Update(kClientStateKey, new_client_state,
+                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->UpdateCallback(true);
+
+  // Verify the updated data is in the database.
+  auto expected = std::make_unique<DbEntries>();
+  expected->emplace_back(new_client_state);
+  VerifyDataInDb(std::move(expected));
+}
+
+// Verifies deleting data.
+TEST_F(ImpressionStoreTest, Delete) {
+  auto test_data = TestClientStates();
+  test_data.emplace(kClientStateKey, kDefaultClientState);
+  Init(test_data, InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+
+  // Delete the entry.
+  store()->Delete(kClientStateKey,
+                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+
+  // Verify there is no data in the database.
+  VerifyDataInDb(std::make_unique<DbEntries>());
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_types.cc b/chrome/browser/notifications/scheduler/internal/impression_types.cc
new file mode 100644
index 0000000..9c4deb8
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/impression_types.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 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/notifications/scheduler/internal/impression_types.h"
+
+#include <sstream>
+
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+
+namespace notifications {
+
+bool Impression::operator==(const Impression& other) const {
+  return create_time == other.create_time && feedback == other.feedback &&
+         impression == other.impression && integrated == other.integrated &&
+         task_start_time == other.task_start_time && guid == other.guid &&
+         type == other.type;
+}
+
+SuppressionInfo::SuppressionInfo(const base::Time& last_trigger,
+                                 const base::TimeDelta& duration)
+    : last_trigger_time(last_trigger), duration(duration), recover_goal(1) {}
+
+SuppressionInfo::SuppressionInfo(const SuppressionInfo& other) = default;
+
+bool SuppressionInfo::operator==(const SuppressionInfo& other) const {
+  return last_trigger_time == other.last_trigger_time &&
+         duration == other.duration && recover_goal == other.recover_goal;
+}
+
+base::Time SuppressionInfo::ReleaseTime() const {
+  return last_trigger_time + duration;
+}
+
+ClientState::ClientState()
+    : type(SchedulerClientType::kUnknown), current_max_daily_show(0) {}
+
+ClientState::ClientState(const ClientState& other) = default;
+
+ClientState::~ClientState() = default;
+
+bool ClientState::operator==(const ClientState& other) const {
+  return type == other.type &&
+         current_max_daily_show == other.current_max_daily_show &&
+         impressions == other.impressions &&
+         suppression_info == other.suppression_info;
+}
+
+std::string ClientState::DebugPrint() const {
+  std::string log = base::StringPrintf(
+      "Client state: type: %d \n"
+      "current_max_daily_show: %d \n"
+      "impressions.size(): %zu \n",
+      static_cast<int>(type), current_max_daily_show, impressions.size());
+
+  for (const auto& impression : impressions) {
+    std::ostringstream stream;
+    stream << "Impression, create_time:" << impression.create_time << "\n"
+           << " create_time in microseconds:"
+           << impression.create_time.ToDeltaSinceWindowsEpoch().InMicroseconds()
+           << "\n"
+           << "feedback: " << static_cast<int>(impression.feedback) << "\n"
+           << "impression result: " << static_cast<int>(impression.impression)
+           << " \n"
+           << "integrated: " << impression.integrated << "\n"
+           << "task start time: "
+           << static_cast<int>(impression.task_start_time) << "\n"
+           << "guid: " << impression.guid << "\n"
+           << "type: " << static_cast<int>(impression.type);
+    log += stream.str();
+  }
+
+  if (suppression_info.has_value()) {
+    std::ostringstream stream;
+    stream << "Suppression info, last_trigger_time:"
+           << suppression_info->last_trigger_time << "\n"
+           << "duration:" << suppression_info->duration << "\n"
+           << "recover_goal:" << suppression_info->recover_goal;
+    log += stream.str();
+  }
+
+  return log;
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_types.h b/chrome/browser/notifications/scheduler/internal/impression_types.h
new file mode 100644
index 0000000..37a8173f6
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/impression_types.h
@@ -0,0 +1,110 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_TYPES_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_TYPES_H_
+
+#include <deque>
+#include <map>
+#include <string>
+
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace notifications {
+
+// Contains data to determine when a notification should be shown to the user
+// and the user impression towards this notification.
+//
+// Life cycle:
+// 1. Created after the notification is shown to the user.
+// 2. |feedback| is set after the user interacts with the notification.
+// 3. Notification scheduler API consumer gets the user feedback and generates
+// an impression result, which may affect notification exposure.
+// 4. The impression is deleted after it expires.
+struct Impression {
+  bool operator==(const Impression& other) const;
+
+  // Creation timestamp.
+  base::Time create_time;
+
+  // The user feedback on the notification, each notification will have at most
+  // one feedback. Sets after the user interacts with the notification.
+  UserFeedback feedback = UserFeedback::kNoFeedback;
+
+  // The impression type. The client of a notification type takes one or several
+  // user feedbacks as input and generate a user impression, which will
+  // eventually affect the rate to deliver notifications to the user.
+  ImpressionResult impression = ImpressionResult::kInvalid;
+
+  // If the user feedback is used in computing the current notification deliver
+  // rate.
+  bool integrated = false;
+
+  // The task start time when this impression is generated.
+  SchedulerTaskTime task_start_time = SchedulerTaskTime::kUnknown;
+
+  // The unique identifier of the notification.
+  std::string guid;
+
+  // The type of the notification. Not persisted to disk, set after database
+  // initialized.
+  // TODO(xingliu): Consider to persist this as well.
+  SchedulerClientType type = SchedulerClientType::kUnknown;
+};
+
+// Contains details about supression and recovery after suppression expired.
+struct SuppressionInfo {
+  SuppressionInfo(const base::Time& last_trigger,
+                  const base::TimeDelta& duration);
+  SuppressionInfo(const SuppressionInfo& other);
+  ~SuppressionInfo() = default;
+  bool operator==(const SuppressionInfo& other) const;
+
+  // Time that the suppression should release.
+  base::Time ReleaseTime() const;
+
+  // The last supression trigger time.
+  base::Time last_trigger_time;
+
+  // The duration for the suppression.
+  base::TimeDelta duration;
+
+  // |current_max_daily_show| will change to this after the suppression
+  // expired.
+  int recover_goal;
+};
+
+// Stores the global states about how often the notification can be shown
+// to the user and the history of user interactions to a particular notification
+// client.
+struct ClientState {
+  using Impressions = std::deque<Impression>;
+  ClientState();
+  explicit ClientState(const ClientState& other);
+  ~ClientState();
+
+  bool operator==(const ClientState& other) const;
+
+  // Dumps data for debugging.
+  std::string DebugPrint() const;
+
+  // The type of notification using the scheduler.
+  SchedulerClientType type;
+
+  // The maximum number of notifications shown to the user for this type. May
+  // change if the user interacts with the notification.
+  int current_max_daily_show;
+
+  // A list of user impression history. Sorted by creation time.
+  Impressions impressions;
+
+  // Suppression details, no value if there is currently no suppression.
+  base::Optional<SuppressionInfo> suppression_info;
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc
new file mode 100644
index 0000000..70d1d16
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc
@@ -0,0 +1,56 @@
+// Copyright 2019 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/notifications/scheduler/internal/init_aware_scheduler.h"
+
+#include "base/bind.h"
+#include "chrome/browser/notifications/scheduler/public/notification_params.h"
+
+namespace notifications {
+
+InitAwareNotificationScheduler::InitAwareNotificationScheduler(
+    std::unique_ptr<NotificationScheduler> impl)
+    : impl_(std::move(impl)), weak_ptr_factory_(this) {}
+
+InitAwareNotificationScheduler::~InitAwareNotificationScheduler() = default;
+
+void InitAwareNotificationScheduler::Init(InitCallback init_callback) {
+  DCHECK(!init_success_.has_value());
+  impl_->Init(base::BindOnce(&InitAwareNotificationScheduler::OnInitialized,
+                             weak_ptr_factory_.GetWeakPtr(),
+                             std::move(init_callback)));
+}
+void InitAwareNotificationScheduler::Schedule(
+    std::unique_ptr<NotificationParams> params) {
+  if (init_success_.has_value() && *init_success_) {
+    impl_->Schedule(std::move(params));
+    return;
+  }
+
+  if (init_success_.has_value() && !*init_success_)
+    return;
+
+  cached_closures_.emplace_back(
+      base::BindOnce(&InitAwareNotificationScheduler::Schedule,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(params)));
+}
+
+void InitAwareNotificationScheduler::OnInitialized(InitCallback init_callback,
+                                                   bool success) {
+  init_success_ = success;
+  if (!success) {
+    cached_closures_.clear();
+    std::move(init_callback).Run(false);
+    return;
+  }
+
+  // Flush all cached calls.
+  for (auto it = cached_closures_.begin(); it != cached_closures_.end(); ++it) {
+    std::move(*it).Run();
+  }
+  cached_closures_.clear();
+  std::move(init_callback).Run(true);
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h
new file mode 100644
index 0000000..fde560e53
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h
@@ -0,0 +1,55 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_INIT_AWARE_SCHEDULER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_INIT_AWARE_SCHEDULER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_scheduler.h"
+
+namespace notifications {
+
+struct NotificationParams;
+
+// NotificationScheduler implementation that caches NotificationScheduler API
+// calls and flush them to the actual implementation after initialization
+// succeeded.
+class InitAwareNotificationScheduler : public NotificationScheduler {
+ public:
+  explicit InitAwareNotificationScheduler(
+      std::unique_ptr<NotificationScheduler> impl);
+  ~InitAwareNotificationScheduler() override;
+
+ private:
+  // NotificationScheduler implementation.
+  void Init(InitCallback init_callback) override;
+  void Schedule(
+      std::unique_ptr<NotificationParams> notification_params) override;
+
+  // Called after initialization is done.
+  void OnInitialized(InitCallback init_callback, bool success);
+
+  // Whether the initialization is successful. No value if initialization is not
+  // finished.
+  base::Optional<bool> init_success_;
+
+  // Cached calls.
+  std::vector<base::OnceClosure> cached_closures_;
+
+  // Actual implementation.
+  std::unique_ptr<NotificationScheduler> impl_;
+
+  base::WeakPtrFactory<InitAwareNotificationScheduler> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(InitAwareNotificationScheduler);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_INIT_AWARE_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc
new file mode 100644
index 0000000..8c9b690
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc
@@ -0,0 +1,127 @@
+// Copyright 2019 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/notifications/scheduler/internal/init_aware_scheduler.h"
+
+#include <memory>
+
+#include "base/bind_helpers.h"
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/notifications/scheduler/public/notification_params.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::InSequence;
+using testing::Invoke;
+
+namespace notifications {
+namespace {
+
+class MockNotificationScheduler : public NotificationScheduler {
+ public:
+  MockNotificationScheduler() = default;
+  ~MockNotificationScheduler() override = default;
+
+  MOCK_METHOD1(Init, void(InitCallback));
+  MOCK_METHOD1(Schedule, void(std::unique_ptr<NotificationParams>));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockNotificationScheduler);
+};
+
+class InitAwareNotificationSchedulerTest : public testing::Test {
+ public:
+  InitAwareNotificationSchedulerTest() : scheduler_impl_(nullptr) {}
+  ~InitAwareNotificationSchedulerTest() override = default;
+
+  void SetUp() override {
+    auto scheduler = std::make_unique<MockNotificationScheduler>();
+    scheduler_impl_ = scheduler.get();
+    init_aware_scheduler_ =
+        std::make_unique<InitAwareNotificationScheduler>(std::move(scheduler));
+  }
+
+ protected:
+  std::unique_ptr<NotificationParams> BuildParams() {
+    return std::make_unique<NotificationParams>(
+        SchedulerClientType::kUnknown, NotificationData(), ScheduleParams());
+  }
+
+  NotificationScheduler* init_aware_scheduler() {
+    return init_aware_scheduler_.get();
+  }
+  MockNotificationScheduler* scheduler_impl() { return scheduler_impl_; }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  MockNotificationScheduler* scheduler_impl_;
+  std::unique_ptr<NotificationScheduler> init_aware_scheduler_;
+
+  DISALLOW_COPY_AND_ASSIGN(InitAwareNotificationSchedulerTest);
+};
+
+// Checks std::unique_ptr<NotificationParams> has specific guid.
+MATCHER_P(GuidIs, expected_guid, "") {
+  return arg->guid == expected_guid;
+}
+
+// Verifies cached calls are flushed into the actual implementation.
+TEST_F(InitAwareNotificationSchedulerTest, FlushCachedCalls) {
+  auto params = BuildParams();
+  std::string guid = params->guid;
+  EXPECT_FALSE(guid.empty());
+  {
+    InSequence sequence;
+    EXPECT_CALL(*scheduler_impl(), Init(_))
+        .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) {
+          std::move(cb).Run(true /*success*/);
+        }));
+    EXPECT_CALL(*scheduler_impl(), Schedule(GuidIs(guid)));
+
+    // Schedule() call before Init() will be cached.
+    init_aware_scheduler()->Schedule(std::move(params));
+    init_aware_scheduler()->Init(base::DoNothing());
+  }
+}
+
+// Verifies that API calls after successful initialization will call into the
+// actual implementation.
+TEST_F(InitAwareNotificationSchedulerTest, CallAfterInitSuccess) {
+  auto params = BuildParams();
+  std::string guid = params->guid;
+  EXPECT_FALSE(guid.empty());
+  {
+    InSequence sequence;
+    EXPECT_CALL(*scheduler_impl(), Init(_))
+        .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) {
+          std::move(cb).Run(true /*success*/);
+        }));
+    EXPECT_CALL(*scheduler_impl(), Schedule(GuidIs(guid)));
+
+    // Schedule() call after Init().
+    init_aware_scheduler()->Init(base::DoNothing());
+    init_aware_scheduler()->Schedule(std::move(params));
+  }
+}
+
+// Verifies no calls are flushed to actual implementation if initialization
+// failed.
+TEST_F(InitAwareNotificationSchedulerTest, NoFlushOnInitFailure) {
+  auto params1 = BuildParams();
+  auto params2 = BuildParams();
+
+  EXPECT_CALL(*scheduler_impl(), Init(_))
+      .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) {
+        std::move(cb).Run(false /*success*/);
+      }));
+  EXPECT_CALL(*scheduler_impl(), Schedule(_)).Times(0);
+
+  init_aware_scheduler()->Schedule(std::move(params1));
+  init_aware_scheduler()->Init(base::DoNothing());
+  init_aware_scheduler()->Schedule(std::move(params2));
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_entry.cc b/chrome/browser/notifications/scheduler/internal/notification_entry.cc
new file mode 100644
index 0000000..f793058
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_entry.cc
@@ -0,0 +1,29 @@
+// Copyright 2019 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/notifications/scheduler/internal/notification_entry.h"
+
+#include <utility>
+
+namespace notifications {
+
+NotificationEntry::NotificationEntry()
+    : NotificationEntry(SchedulerClientType::kUnknown, std::string()) {}
+
+NotificationEntry::NotificationEntry(SchedulerClientType type,
+                                     const std::string& guid)
+    : type(type), guid(guid), create_time(base::Time::Now()) {}
+
+NotificationEntry::NotificationEntry(const NotificationEntry& other) = default;
+
+bool NotificationEntry::operator==(const NotificationEntry& other) const {
+  return type == other.type && guid == other.guid &&
+         create_time == other.create_time &&
+         notification_data == other.notification_data &&
+         schedule_params == other.schedule_params;
+}
+
+NotificationEntry::~NotificationEntry() = default;
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_entry.h b/chrome/browser/notifications/scheduler/internal/notification_entry.h
new file mode 100644
index 0000000..db27e00a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_entry.h
@@ -0,0 +1,44 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_ENTRY_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_ENTRY_H_
+
+#include <string>
+
+#include "base/time/time.h"
+#include "chrome/browser/notifications/scheduler/public/notification_data.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+#include "chrome/browser/notifications/scheduler/public/schedule_params.h"
+
+namespace notifications {
+
+// Represents the in-memory counterpart of scheduled notification database
+// record.
+struct NotificationEntry {
+  NotificationEntry();
+  NotificationEntry(SchedulerClientType type, const std::string& guid);
+  NotificationEntry(const NotificationEntry& other);
+  bool operator==(const NotificationEntry& other) const;
+  ~NotificationEntry();
+
+  // The type of the notification.
+  SchedulerClientType type;
+
+  // The unique id of the notification database entry.
+  std::string guid;
+
+  // Creation timestamp.
+  base::Time create_time;
+
+  // Contains information to construct the notification.
+  NotificationData notification_data;
+
+  // Scheduling details.
+  ScheduleParams schedule_params;
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc
new file mode 100644
index 0000000..da199db4
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc
@@ -0,0 +1,26 @@
+// Copyright 2019 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/notifications/scheduler/internal/notification_schedule_service_impl.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_scheduler.h"
+#include "chrome/browser/notifications/scheduler/public/notification_params.h"
+
+namespace notifications {
+
+NotificationScheduleServiceImpl::NotificationScheduleServiceImpl(
+    std::unique_ptr<NotificationScheduler> scheduler)
+    : scheduler_(std::move(scheduler)) {}
+
+NotificationScheduleServiceImpl::~NotificationScheduleServiceImpl() = default;
+
+void NotificationScheduleServiceImpl::Schedule(
+    std::unique_ptr<NotificationParams> notification_params) {
+  scheduler_->Schedule(std::move(notification_params));
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h
new file mode 100644
index 0000000..802712c
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h
@@ -0,0 +1,37 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/public/notification_schedule_service.h"
+
+namespace notifications {
+
+class NotificationScheduler;
+struct NotificationParams;
+
+class NotificationScheduleServiceImpl : public NotificationScheduleService {
+ public:
+  explicit NotificationScheduleServiceImpl(
+      std::unique_ptr<NotificationScheduler> scheduler);
+  ~NotificationScheduleServiceImpl() override;
+
+ private:
+  // NotificationScheduleService implementation.
+  void Schedule(
+      std::unique_ptr<NotificationParams> notification_params) override;
+
+  // Provides the actual notification scheduling functionalities.
+  std::unique_ptr<NotificationScheduler> scheduler_;
+
+  DISALLOW_COPY_AND_ASSIGN(NotificationScheduleServiceImpl);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
new file mode 100644
index 0000000..7bdec94
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
@@ -0,0 +1,226 @@
+// Copyright 2019 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/notifications/scheduler/internal/notification_scheduler.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "chrome/browser/notifications/scheduler/internal/display_decider.h"
+#include "chrome/browser/notifications/scheduler/internal/distribution_policy.h"
+#include "chrome/browser/notifications/scheduler/internal/icon_store.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h"
+#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
+#include "chrome/browser/notifications/scheduler/public/notification_params.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h"
+#include "chrome/browser/notifications/scheduler/public/user_action_handler.h"
+
+namespace notifications {
+namespace {
+
+class NotificationSchedulerImpl;
+
+// Helper class to do async initialization in parallel for multiple subsystem
+// instances.
+class InitHelper {
+ public:
+  using InitCallback = base::OnceCallback<void(bool)>;
+  InitHelper() : weak_ptr_factory_(this) {}
+
+  ~InitHelper() = default;
+
+  // Initializes subsystems in notification scheduler, |callback| will be
+  // invoked if all initializations finished or anyone of them failed. The
+  // object should be destroyed along with the |callback|.
+  void Init(
+      NotificationSchedulerContext* context,
+      ScheduledNotificationManager::Delegate* notification_manager_delegate,
+      ImpressionHistoryTracker::Delegate* impression_tracker_delegate,
+      InitCallback callback) {
+    callback_ = std::move(callback);
+    context->icon_store()->Init(base::BindOnce(
+        &InitHelper::OnIconStoreInitialized, weak_ptr_factory_.GetWeakPtr()));
+    context->impression_tracker()->Init(
+        impression_tracker_delegate,
+        base::BindOnce(&InitHelper::OnImpressionTrackerInitialized,
+                       weak_ptr_factory_.GetWeakPtr()));
+    context->notification_manager()->Init(
+        notification_manager_delegate,
+        base::BindOnce(&InitHelper::OnNotificationManagerInitialized,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+ private:
+  void OnIconStoreInitialized(bool success) {
+    icon_store_initialzed_ = success;
+    MaybeFinishInitialization();
+  }
+
+  void OnImpressionTrackerInitialized(bool success) {
+    impression_tracker_initialzed_ = success;
+    MaybeFinishInitialization();
+  }
+
+  void OnNotificationManagerInitialized(bool success) {
+    notification_manager_initialized_ = success;
+    MaybeFinishInitialization();
+  }
+
+  void MaybeFinishInitialization() {
+    bool all_finished = icon_store_initialzed_.has_value() &&
+                        impression_tracker_initialzed_.has_value() &&
+                        notification_manager_initialized_.has_value();
+    // Notify the initialization result when all subcomponents are initialized.
+    if (!all_finished)
+      return;
+
+    bool success = icon_store_initialzed_.value() &&
+                   impression_tracker_initialzed_.value() &&
+                   notification_manager_initialized_.value();
+    std::move(callback_).Run(success);
+  }
+
+  InitCallback callback_;
+  base::Optional<bool> icon_store_initialzed_;
+  base::Optional<bool> impression_tracker_initialzed_;
+  base::Optional<bool> notification_manager_initialized_;
+
+  base::WeakPtrFactory<InitHelper> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(InitHelper);
+};
+
+// Implementation of NotificationScheduler.
+class NotificationSchedulerImpl
+    : public NotificationScheduler,
+      public NotificationBackgroundTaskScheduler::Handler,
+      public ScheduledNotificationManager::Delegate,
+      public ImpressionHistoryTracker::Delegate,
+      public UserActionHandler {
+ public:
+  NotificationSchedulerImpl(
+      std::unique_ptr<NotificationSchedulerContext> context)
+      : context_(std::move(context)), weak_ptr_factory_(this) {}
+
+  ~NotificationSchedulerImpl() override = default;
+
+ private:
+  // NotificationScheduler implementation.
+  void Init(InitCallback init_callback) override {
+    auto helper = std::make_unique<InitHelper>();
+    auto* helper_ptr = helper.get();
+    helper_ptr->Init(
+        context_.get(), this, this,
+        base::BindOnce(&NotificationSchedulerImpl::OnInitialized,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(helper),
+                       std::move(init_callback)));
+  }
+
+  void Schedule(
+      std::unique_ptr<NotificationParams> notification_params) override {
+    context_->notification_manager()->ScheduleNotification(
+        std::move(notification_params));
+  }
+
+  void OnInitialized(std::unique_ptr<InitHelper>,
+                     InitCallback init_callback,
+                     bool success) {
+    // TODO(xingliu): Inform the clients about initialization results and tear
+    // down internal components.
+    std::move(init_callback).Run(success);
+  }
+
+  // NotificationBackgroundTaskScheduler::Handler implementation.
+  void OnStartTask() override {
+    // Updates the impression data to compute daily notification shown budget.
+    context_->impression_tracker()->AnalyzeImpressionHistory();
+
+    // TODO(xingliu): Pass SchedulerTaskTime from background task.
+    FindNotificationToShow(SchedulerTaskTime::kMorning);
+
+    // Schedule the next background task based on scheduled notifications.
+    ScheduleBackgroundTask();
+  }
+
+  void OnStopTask() override { ScheduleBackgroundTask(); }
+
+  // ScheduledNotificationManager::Delegate implementation.
+  void DisplayNotification(
+      std::unique_ptr<NotificationEntry> notification) override {
+    // TODO(xingliu): Inform the clients and show the notification.
+    NOTIMPLEMENTED();
+  }
+
+  // ImpressionHistoryTracker::Delegate implementation.
+  void OnImpressionUpdated() override { ScheduleBackgroundTask(); }
+
+  void FindNotificationToShow(SchedulerTaskTime task_start_time) {
+    DisplayDecider::Results results;
+    ScheduledNotificationManager::Notifications notifications;
+    context_->notification_manager()->GetAllNotifications(&notifications);
+
+    DisplayDecider::ClientStates client_states;
+    context_->impression_tracker()->GetClientStates(&client_states);
+
+    std::vector<SchedulerClientType> clients;
+    context_->client_registrar()->GetRegisteredClients(&clients);
+
+    context_->display_decider()->FindNotificationsToShow(
+        context_->config(), std::move(clients), DistributionPolicy::Create(),
+        task_start_time, std::move(notifications), std::move(client_states),
+        &results);
+
+    // TODO(xingliu): Update impression data after notification shown.
+    // See https://crbug.com/965133.
+    for (const auto& guid : results) {
+      context_->notification_manager()->DisplayNotification(guid);
+    }
+  }
+
+  void ScheduleBackgroundTask() {
+    // TODO(xingliu): Implements a class to determine the next background task
+    // based on scheduled notification data.
+    NOTIMPLEMENTED();
+  }
+
+  void OnClick(const std::string& notification_id) override {
+    context_->impression_tracker()->OnClick(notification_id);
+  }
+
+  void OnActionClick(const std::string& notification_id,
+                     ActionButtonType button_type) override {
+    context_->impression_tracker()->OnActionClick(notification_id, button_type);
+  }
+
+  void OnDismiss(const std::string& notification_id) override {
+    context_->impression_tracker()->OnDismiss(notification_id);
+  }
+
+  std::unique_ptr<NotificationSchedulerContext> context_;
+
+  base::WeakPtrFactory<NotificationSchedulerImpl> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerImpl);
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<NotificationScheduler> NotificationScheduler::Create(
+    std::unique_ptr<NotificationSchedulerContext> context) {
+  return std::make_unique<NotificationSchedulerImpl>(std::move(context));
+}
+
+NotificationScheduler::NotificationScheduler() = default;
+
+NotificationScheduler::~NotificationScheduler() = default;
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.h b/chrome/browser/notifications/scheduler/internal/notification_scheduler.h
new file mode 100644
index 0000000..ad2054c7
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.h
@@ -0,0 +1,43 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+
+namespace notifications {
+
+class NotificationSchedulerContext;
+struct NotificationParams;
+
+// Provides notification scheduling and throttling functionalities. This class
+// glues all the subsystems together for notification scheduling system.
+class NotificationScheduler {
+ public:
+  using InitCallback = base::OnceCallback<void(bool)>;
+  static std::unique_ptr<NotificationScheduler> Create(
+      std::unique_ptr<NotificationSchedulerContext> context);
+
+  NotificationScheduler();
+  virtual ~NotificationScheduler();
+
+  // Initializes the scheduler.
+  virtual void Init(InitCallback init_callback) = 0;
+
+  // Schedules a notification to show in the future. Throttling logic may apply
+  // based on |notification_params|.
+  virtual void Schedule(
+      std::unique_ptr<NotificationParams> notification_params) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NotificationScheduler);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc
new file mode 100644
index 0000000..b70faa5
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 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/notifications/scheduler/internal/notification_scheduler_context.h"
+
+#include <utility>
+
+#include "chrome/browser/notifications/scheduler/internal/display_decider.h"
+#include "chrome/browser/notifications/scheduler/internal/icon_store.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h"
+
+namespace notifications {
+
+NotificationSchedulerContext::NotificationSchedulerContext(
+    std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar,
+    std::unique_ptr<NotificationBackgroundTaskScheduler> scheduler,
+    std::unique_ptr<IconStore> icon_store,
+    std::unique_ptr<ImpressionHistoryTracker> impression_tracker,
+    std::unique_ptr<ScheduledNotificationManager> notification_manager,
+    std::unique_ptr<DisplayDecider> display_decider,
+    std::unique_ptr<SchedulerConfig> config)
+    : client_registrar_(std::move(client_registrar)),
+      background_task_scheduler_(std::move(scheduler)),
+      impression_tracker_(std::move(impression_tracker)),
+      notification_manager_(std::move(notification_manager)),
+      display_decider_(std::move(display_decider)),
+      config_(std::move(config)) {}
+
+NotificationSchedulerContext::~NotificationSchedulerContext() = default;
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h
new file mode 100644
index 0000000..86aa260
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h
@@ -0,0 +1,88 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_CONTEXT_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_CONTEXT_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace notifications {
+
+class DisplayDecider;
+class IconStore;
+class ImpressionHistoryTracker;
+class NotificationBackgroundTaskScheduler;
+class NotificationSchedulerClientRegistrar;
+class ScheduledNotificationManager;
+struct SchedulerConfig;
+
+// Context that contains necessary components needed by the notification
+// scheduler to perform tasks.
+class NotificationSchedulerContext {
+ public:
+  NotificationSchedulerContext(
+      std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar,
+      std::unique_ptr<NotificationBackgroundTaskScheduler> scheduler,
+      std::unique_ptr<IconStore> icon_store,
+      std::unique_ptr<ImpressionHistoryTracker> impression_tracker,
+      std::unique_ptr<ScheduledNotificationManager> notification_manager,
+      std::unique_ptr<DisplayDecider> display_decider,
+      std::unique_ptr<SchedulerConfig> config);
+  ~NotificationSchedulerContext();
+
+  NotificationSchedulerClientRegistrar* client_registrar() {
+    return client_registrar_.get();
+  }
+
+  NotificationBackgroundTaskScheduler* background_task_scheduler() {
+    return background_task_scheduler_.get();
+  }
+
+  IconStore* icon_store() { return icon_store_.get(); }
+
+  ImpressionHistoryTracker* impression_tracker() {
+    return impression_tracker_.get();
+  }
+
+  ScheduledNotificationManager* notification_manager() {
+    return notification_manager_.get();
+  }
+
+  DisplayDecider* display_decider() { return display_decider_.get(); }
+
+  const SchedulerConfig* config() const { return config_.get(); }
+
+ private:
+  // Holds a list of clients using the notification scheduler system.
+  std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar_;
+
+  // Used to schedule background task in OS level.
+  std::unique_ptr<NotificationBackgroundTaskScheduler>
+      background_task_scheduler_;
+
+  // Stores notification icons.
+  std::unique_ptr<IconStore> icon_store_;
+
+  // Tracks user impressions towards specific notification type.
+  std::unique_ptr<ImpressionHistoryTracker> impression_tracker_;
+
+  // Stores all scheduled notifications.
+  std::unique_ptr<ScheduledNotificationManager> notification_manager_;
+
+  // Helper class to decide which notification should be displayed to the user.
+  std::unique_ptr<DisplayDecider> display_decider_;
+
+  // System configuration.
+  std::unique_ptr<SchedulerConfig> config_;
+
+  DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerContext);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_CONTEXT_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_store.cc b/chrome/browser/notifications/scheduler/internal/notification_store.cc
new file mode 100644
index 0000000..74c9dd1
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_store.cc
@@ -0,0 +1,102 @@
+// Copyright 2019 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/notifications/scheduler/internal/notification_store.h"
+
+#include "base/bind.h"
+#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h"
+
+namespace leveldb_proto {
+void DataToProto(notifications::NotificationEntry* entry,
+                 notifications::proto::NotificationEntry* proto) {
+  NotificationEntryToProto(entry, proto);
+}
+
+void ProtoToData(notifications::proto::NotificationEntry* proto,
+                 notifications::NotificationEntry* entry) {
+  NotificationEntryFromProto(proto, entry);
+}
+}  // namespace leveldb_proto
+
+namespace notifications {
+
+NotificationStore::NotificationStore(
+    std::unique_ptr<leveldb_proto::ProtoDatabase<proto::NotificationEntry,
+                                                 NotificationEntry>> db)
+    : db_(std::move(db)), weak_ptr_factory_(this) {}
+
+NotificationStore::~NotificationStore() = default;
+
+void NotificationStore::InitAndLoad(LoadCallback callback) {
+  db_->Init(base::BindOnce(&NotificationStore::OnDbInitialized,
+                           weak_ptr_factory_.GetWeakPtr(),
+                           std::move(callback)));
+}
+
+void NotificationStore::OnDbInitialized(
+    LoadCallback callback,
+    leveldb_proto::Enums::InitStatus status) {
+  if (status != leveldb_proto::Enums::InitStatus::kOK) {
+    std::move(callback).Run(false, Entries());
+    return;
+  }
+
+  // Load the data after a successful initialization.
+  db_->LoadEntries(base::BindOnce(&NotificationStore::OnDataLoaded,
+                                  weak_ptr_factory_.GetWeakPtr(),
+                                  std::move(callback)));
+}
+
+void NotificationStore::OnDataLoaded(
+    LoadCallback callback,
+    bool success,
+    std::unique_ptr<EntryVector> entry_vector) {
+  // The database failed to load.
+  if (!success) {
+    std::move(callback).Run(false, Entries());
+    return;
+  }
+
+  // Success to load but no data.
+  if (!entry_vector) {
+    std::move(callback).Run(true, Entries());
+    return;
+  }
+
+  // Load data.
+  Entries entries;
+  for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) {
+    std::unique_ptr<NotificationEntry> notification_entry =
+        std::make_unique<NotificationEntry>(std::move(*it));
+    entries.emplace_back(std::move(notification_entry));
+  }
+
+  std::move(callback).Run(true, std::move(entries));
+}
+
+void NotificationStore::Add(const std::string& key,
+                            const NotificationEntry& entry,
+                            UpdateCallback callback) {
+  Update(key, entry, std::move(callback));
+}
+
+void NotificationStore::Update(const std::string& key,
+                               const NotificationEntry& entry,
+                               UpdateCallback callback) {
+  auto entries_to_save = std::make_unique<KeyEntryVector>();
+  entries_to_save->emplace_back(std::make_pair(key, entry));
+  db_->UpdateEntries(std::move(entries_to_save),
+                     std::make_unique<KeyVector>() /*keys_to_remove*/,
+                     std::move(callback));
+}
+
+void NotificationStore::Delete(const std::string& key,
+                               UpdateCallback callback) {
+  auto keys_to_delete = std::make_unique<KeyVector>();
+  keys_to_delete->emplace_back(key);
+  db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/,
+                     std::move(keys_to_delete), std::move(callback));
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_store.h b/chrome/browser/notifications/scheduler/internal/notification_store.h
new file mode 100644
index 0000000..cf032818
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_store.h
@@ -0,0 +1,75 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_STORE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_STORE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/notifications/proto/notification_entry.pb.h"
+#include "chrome/browser/notifications/scheduler/internal/collection_store.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+#include "components/leveldb_proto/public/proto_database.h"
+
+// Forward declaration for proto conversion.
+namespace leveldb_proto {
+void DataToProto(notifications::NotificationEntry* entry,
+                 notifications::proto::NotificationEntry* proto);
+
+void ProtoToData(notifications::proto::NotificationEntry* proto,
+                 notifications::NotificationEntry* entry);
+}  // namespace leveldb_proto
+
+namespace notifications {
+
+class NotificationStore : public CollectionStore<NotificationEntry> {
+ public:
+  NotificationStore(
+      std::unique_ptr<leveldb_proto::ProtoDatabase<proto::NotificationEntry,
+                                                   NotificationEntry>> db);
+  ~NotificationStore() override;
+
+ private:
+  using KeyEntryVector = std::vector<std::pair<std::string, NotificationEntry>>;
+  using KeyVector = std::vector<std::string>;
+  using EntryVector = std::vector<NotificationEntry>;
+
+  // CollectionStore<NotificationEntry> implementation.
+  void InitAndLoad(LoadCallback callback) override;
+  void Add(const std::string& key,
+           const NotificationEntry& entry,
+           UpdateCallback callback) override;
+  void Update(const std::string& key,
+              const NotificationEntry& entry,
+              UpdateCallback callback) override;
+  void Delete(const std::string& key, UpdateCallback callback) override;
+
+  // Called when the proto database is initialized but no yet loading the data
+  // into memory.
+  void OnDbInitialized(LoadCallback callback,
+                       leveldb_proto::Enums::InitStatus status);
+
+  // Called when data is loaded from |db_|.
+  void OnDataLoaded(LoadCallback callback,
+                    bool success,
+                    std::unique_ptr<EntryVector> entry_vector);
+
+  // Level db instance to persist data.
+  std::unique_ptr<
+      leveldb_proto::ProtoDatabase<proto::NotificationEntry, NotificationEntry>>
+      db_;
+
+  base::WeakPtrFactory<NotificationStore> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(NotificationStore);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc b/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc
new file mode 100644
index 0000000..83f4517
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc
@@ -0,0 +1,176 @@
+// Copyright 2019 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/notifications/scheduler/internal/notification_store.h"
+
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h"
+#include "chrome/browser/notifications/scheduler/test/test_utils.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using leveldb_proto::test::FakeDB;
+using InitStatus = leveldb_proto::Enums::InitStatus;
+using Entries = notifications::NotificationStore::Entries;
+using TestNotificationEntries =
+    std::map<std::string, notifications::NotificationEntry>;
+using DbEntries = std::vector<notifications::NotificationEntry>;
+using DbEntriesPtr =
+    std::unique_ptr<std::vector<notifications::NotificationEntry>>;
+
+namespace notifications {
+namespace {
+
+const char kGuid[] = "1234";
+
+class NotificationStoreTest : public testing::Test {
+ public:
+  NotificationStoreTest() : load_result_(false) {}
+  ~NotificationStoreTest() override = default;
+
+  void SetUp() override {}
+
+ protected:
+  void Init(const TestNotificationEntries& test_data, InitStatus status) {
+    CreateTestProtos(test_data);
+    auto db =
+        std::make_unique<FakeDB<proto::NotificationEntry, NotificationEntry>>(
+            &db_protos_);
+    db_ = db.get();
+    store_ = std::make_unique<NotificationStore>(std::move(db));
+    store_->InitAndLoad(base::BindOnce(&NotificationStoreTest::OnEntriesLoaded,
+                                       base::Unretained(this)));
+    db_->InitStatusCallback(status);
+  }
+
+  bool load_result() const { return load_result_; }
+  const Entries& loaded_entries() const { return loaded_entries_; }
+  FakeDB<proto::NotificationEntry, NotificationEntry>* db() { return db_; }
+  CollectionStore<NotificationEntry>* store() { return store_.get(); }
+
+  void VerifyDataInDb(DbEntriesPtr expected) {
+    db_->LoadEntries(base::BindOnce(
+        [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) {
+          EXPECT_TRUE(success);
+          DCHECK(entries);
+          DCHECK(expected);
+          EXPECT_EQ(entries->size(), expected->size());
+          for (size_t i = 0, size = entries->size(); i < size; ++i) {
+            auto& entry = (*entries)[i];
+            auto& expected_entry = (*expected)[i];
+            EXPECT_EQ(entry, expected_entry)
+                << " \n Output: " << test::DebugString(&entry)
+                << " \n Expected: " << test::DebugString(&expected_entry);
+          }
+        },
+        std::move(expected)));
+    db_->LoadCallback(true);
+  }
+
+ private:
+  // Push data into |db_|.
+  void CreateTestProtos(const TestNotificationEntries& test_data) {
+    for (const auto& pair : test_data) {
+      const auto& key = pair.first;
+      auto entry = pair.second;
+      proto::NotificationEntry proto;
+      NotificationEntryToProto(&entry, &proto);
+      db_protos_.emplace(key, proto);
+    }
+  }
+
+  void OnEntriesLoaded(bool success, Entries entries) {
+    load_result_ = success;
+    loaded_entries_ = std::move(entries);
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  // Database test objects.
+  FakeDB<proto::NotificationEntry, NotificationEntry>* db_;
+  std::map<std::string, proto::NotificationEntry> db_protos_;
+
+  std::unique_ptr<CollectionStore<NotificationEntry>> store_;
+  Entries loaded_entries_;
+  bool load_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(NotificationStoreTest);
+};
+
+// Verifies initialization with empty database.
+TEST_F(NotificationStoreTest, Init) {
+  Init(TestNotificationEntries(), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(loaded_entries().empty());
+}
+
+// Initialize non-empty database should success.
+TEST_F(NotificationStoreTest, InitSuccessWithData) {
+  auto test_data = TestNotificationEntries();
+  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
+  bool success =
+      base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time);
+  DCHECK(success);
+  test_data.emplace(kGuid, entry);
+  Init(test_data, InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_EQ(loaded_entries().size(), 1u);
+  EXPECT_EQ(*loaded_entries().back(), entry);
+}
+
+// Failed database initialization will result in error.
+TEST_F(NotificationStoreTest, InitFailed) {
+  Init(TestNotificationEntries(), InitStatus::kCorrupt);
+  EXPECT_EQ(load_result(), false);
+  EXPECT_TRUE(loaded_entries().empty());
+}
+
+TEST_F(NotificationStoreTest, AddAndUpdate) {
+  Init(TestNotificationEntries(), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(loaded_entries().empty());
+
+  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
+  bool success =
+      base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time);
+  DCHECK(success);
+
+  // Add data to the store and verify the database.
+  store()->Add(kGuid, entry,
+               base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->UpdateCallback(true);
+  auto expected = std::make_unique<DbEntries>();
+  expected->emplace_back(entry);
+  VerifyDataInDb(std::move(expected));
+
+  // Update and verified the new data.
+  entry.notification_data.title = "test_title";
+  expected = std::make_unique<DbEntries>();
+  expected->emplace_back(entry);
+  store()->Update(kGuid, entry,
+                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->UpdateCallback(true);
+  VerifyDataInDb(std::move(expected));
+}
+
+TEST_F(NotificationStoreTest, Delete) {
+  auto test_data = TestNotificationEntries();
+  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
+  test_data.emplace(kGuid, entry);
+  Init(test_data, InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(loaded_entries().size(), 1u);
+
+  // Delete the entry and verify data is deleted.
+  store()->Delete(kGuid, base::DoNothing());
+  db()->UpdateCallback(true);
+  VerifyDataInDb(std::make_unique<DbEntries>());
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
new file mode 100644
index 0000000..4fa2624
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
@@ -0,0 +1,327 @@
+// Copyright 2019 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/notifications/scheduler/internal/proto_conversion.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+
+namespace notifications {
+
+namespace {
+
+// Helper method to convert base::TimeDelta to integer for serialization. Loses
+// precision beyond miliseconds.
+int64_t TimeDeltaToMilliseconds(const base::TimeDelta& delta) {
+  return delta.InMilliseconds();
+}
+
+// Helper method to convert serialized time delta as integer to base::TimeDelta
+// for deserialization. Loses precision beyond miliseconds.
+base::TimeDelta MillisecondsToTimeDelta(int64_t serialized_delat_ms) {
+  return base::TimeDelta::FromMilliseconds(serialized_delat_ms);
+}
+
+// Helper method to convert base::Time to integer for serialization. Loses
+// precision beyond miliseconds.
+int64_t TimeToMilliseconds(const base::Time& time) {
+  return time.ToDeltaSinceWindowsEpoch().InMilliseconds();
+}
+
+// Helper method to convert serialized time as integer to base::Time for
+// deserialization. Loses precision beyond miliseconds.
+base::Time MillisecondsToTime(int64_t serialized_time_ms) {
+  return base::Time::FromDeltaSinceWindowsEpoch(
+      base::TimeDelta::FromMilliseconds(serialized_time_ms));
+}
+
+// Converts SchedulerClientType to its associated enum in proto buffer.
+proto::SchedulerClientType ToSchedulerClientType(SchedulerClientType type) {
+  switch (type) {
+    case SchedulerClientType::kTest1:
+      return proto::SchedulerClientType::TEST_1;
+    case SchedulerClientType::kTest2:
+      return proto::SchedulerClientType::TEST_2;
+    case SchedulerClientType::kTest3:
+      return proto::SchedulerClientType::TEST_3;
+    case SchedulerClientType::kUnknown:
+      return proto::SchedulerClientType::UNKNOWN;
+  }
+  NOTREACHED();
+}
+
+// Converts SchedulerClientType from its associated enum in proto buffer.
+SchedulerClientType FromSchedulerClientType(
+    proto::SchedulerClientType proto_type) {
+  switch (proto_type) {
+    case proto::SchedulerClientType::TEST_1:
+      return SchedulerClientType::kTest1;
+    case proto::SchedulerClientType::TEST_2:
+      return SchedulerClientType::kTest2;
+    case proto::SchedulerClientType::TEST_3:
+      return SchedulerClientType::kTest3;
+    case proto::SchedulerClientType::UNKNOWN:
+      return SchedulerClientType::kUnknown;
+  }
+  NOTREACHED();
+}
+
+// Converts UserFeedback to its associated enum in proto buffer.
+proto::Impression_UserFeedback ToUserFeedback(UserFeedback feedback) {
+  switch (feedback) {
+    case UserFeedback::kNoFeedback:
+      return proto::Impression_UserFeedback_NO_FEEDBACK;
+    case UserFeedback::kHelpful:
+      return proto::Impression_UserFeedback_HELPFUL;
+    case UserFeedback::kNotHelpful:
+      return proto::Impression_UserFeedback_NOT_HELPFUL;
+    case UserFeedback::kClick:
+      return proto::Impression_UserFeedback_CLICK;
+    case UserFeedback::kDismiss:
+      return proto::Impression_UserFeedback_DISMISS;
+    case UserFeedback::kIgnore:
+      return proto::Impression_UserFeedback_IGNORE;
+  }
+  NOTREACHED();
+}
+
+// Converts UserFeedback from its associated enum in proto buffer.
+UserFeedback FromUserFeedback(proto::Impression_UserFeedback feedback) {
+  switch (feedback) {
+    case proto::Impression_UserFeedback_NO_FEEDBACK:
+      return UserFeedback::kNoFeedback;
+    case proto::Impression_UserFeedback_HELPFUL:
+      return UserFeedback::kHelpful;
+    case proto::Impression_UserFeedback_NOT_HELPFUL:
+      return UserFeedback::kNotHelpful;
+    case proto::Impression_UserFeedback_CLICK:
+      return UserFeedback::kClick;
+    case proto::Impression_UserFeedback_DISMISS:
+      return UserFeedback::kDismiss;
+    case proto::Impression_UserFeedback_IGNORE:
+      return UserFeedback::kIgnore;
+  }
+  NOTREACHED();
+}
+
+// Converts ImpressionResult to its associated enum in proto buffer.
+proto::Impression_ImpressionResult ToImpressionResult(ImpressionResult result) {
+  switch (result) {
+    case ImpressionResult::kInvalid:
+      return proto::Impression_ImpressionResult_INVALID;
+    case ImpressionResult::kPositive:
+      return proto::Impression_ImpressionResult_POSITIVE;
+    case ImpressionResult::kNegative:
+      return proto::Impression_ImpressionResult_NEGATIVE;
+    case ImpressionResult::kNeutral:
+      return proto::Impression_ImpressionResult_NEUTRAL;
+  }
+  NOTREACHED();
+}
+
+// Converts ImpressionResult from its associated enum in proto buffer.
+ImpressionResult FromImpressionResult(
+    proto::Impression_ImpressionResult result) {
+  switch (result) {
+    case proto::Impression_ImpressionResult_INVALID:
+      return ImpressionResult::kInvalid;
+    case proto::Impression_ImpressionResult_POSITIVE:
+      return ImpressionResult::kPositive;
+    case proto::Impression_ImpressionResult_NEGATIVE:
+      return ImpressionResult::kNegative;
+    case proto::Impression_ImpressionResult_NEUTRAL:
+      return ImpressionResult::kNeutral;
+  }
+  NOTREACHED();
+}
+
+// Converts ImpressionResult to its associated enum in proto buffer.
+proto::Impression_SchedulerTaskTime ToSchedulerTaskTime(
+    SchedulerTaskTime time) {
+  switch (time) {
+    case SchedulerTaskTime::kUnknown:
+      return proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME;
+    case SchedulerTaskTime::kMorning:
+      return proto::Impression_SchedulerTaskTime_MORNING;
+    case SchedulerTaskTime::kEvening:
+      return proto::Impression_SchedulerTaskTime_EVENING;
+  }
+  NOTREACHED();
+}
+
+// Converts ImpressionResult from its associated enum in proto buffer.
+SchedulerTaskTime FromSchedulerTaskTime(
+    proto::Impression_SchedulerTaskTime time) {
+  switch (time) {
+    case proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME:
+      return SchedulerTaskTime::kUnknown;
+    case proto::Impression_SchedulerTaskTime_MORNING:
+      return SchedulerTaskTime::kMorning;
+    case proto::Impression_SchedulerTaskTime_EVENING:
+      return SchedulerTaskTime::kEvening;
+  }
+  NOTREACHED();
+}
+
+// Converts NotificationData to proto buffer type.
+void NotificationDataToProto(NotificationData* notification_data,
+                             proto::NotificationData* proto) {
+  proto->set_id(notification_data->id);
+  proto->set_title(notification_data->title);
+  proto->set_message(notification_data->message);
+  proto->set_icon_uuid(notification_data->icon_uuid);
+  proto->set_url(notification_data->url);
+}
+
+// Converts NotificationData from proto buffer type.
+void NotificationDataFromProto(proto::NotificationData* proto,
+                               NotificationData* notification_data) {
+  notification_data->id = proto->id();
+  notification_data->title = proto->title();
+  notification_data->message = proto->message();
+  notification_data->icon_uuid = proto->icon_uuid();
+  notification_data->url = proto->url();
+}
+
+// Converts ScheduleParams::Priority to proto buffer type.
+proto::ScheduleParams_Priority ScheduleParamsPriorityToProto(
+    ScheduleParams::Priority priority) {
+  using Priority = ScheduleParams::Priority;
+  switch (priority) {
+    case Priority::kLow:
+      return proto::ScheduleParams_Priority_LOW;
+    case Priority::kHigh:
+      return proto::ScheduleParams_Priority_HIGH;
+    case Priority::kNoThrottle:
+      return proto::ScheduleParams_Priority_NO_THROTTLE;
+  }
+}
+
+// Converts ScheduleParams::Priority from proto buffer type.
+ScheduleParams::Priority ScheduleParamsPriorityFromProto(
+    proto::ScheduleParams_Priority priority) {
+  using Priority = ScheduleParams::Priority;
+  switch (priority) {
+    case proto::ScheduleParams_Priority_LOW:
+      return Priority::kLow;
+    case proto::ScheduleParams_Priority_HIGH:
+      return Priority::kHigh;
+    case proto::ScheduleParams_Priority_NO_THROTTLE:
+      return Priority::kNoThrottle;
+  }
+}
+
+// Converts ScheduleParams to proto buffer type.
+void ScheduleParamsToProto(ScheduleParams* params,
+                           proto::ScheduleParams* proto) {
+  proto->set_priority(ScheduleParamsPriorityToProto(params->priority));
+}
+
+// Converts ScheduleParams from proto buffer type.
+void ScheduleParamsFromProto(proto::ScheduleParams* proto,
+                             ScheduleParams* params) {
+  params->priority = ScheduleParamsPriorityFromProto(proto->priority());
+}
+
+}  // namespace
+
+void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto) {
+  proto->mutable_uuid()->swap(entry->uuid);
+  proto->mutable_icon()->swap(entry->data);
+}
+
+void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry) {
+  DCHECK(proto->has_uuid());
+  DCHECK(proto->has_icon());
+  entry->data.swap(*proto->mutable_icon());
+  entry->uuid.swap(*proto->mutable_uuid());
+}
+
+void ClientStateToProto(ClientState* client_state,
+                        notifications::proto::ClientState* proto) {
+  proto->set_type(ToSchedulerClientType(client_state->type));
+  proto->set_current_max_daily_show(client_state->current_max_daily_show);
+
+  for (const auto& impression : client_state->impressions) {
+    auto* impression_ptr = proto->add_impressions();
+    impression_ptr->set_create_time(TimeToMilliseconds(impression.create_time));
+    impression_ptr->set_feedback(ToUserFeedback(impression.feedback));
+    impression_ptr->set_impression(ToImpressionResult(impression.impression));
+    impression_ptr->set_integrated(impression.integrated);
+    impression_ptr->set_task_start_time(
+        ToSchedulerTaskTime(impression.task_start_time));
+    impression_ptr->set_guid(impression.guid);
+  }
+
+  if (client_state->suppression_info.has_value()) {
+    const auto& suppression = *client_state->suppression_info;
+    auto* suppression_proto = proto->mutable_suppression_info();
+    suppression_proto->set_last_trigger_time(
+        TimeToMilliseconds(suppression.last_trigger_time));
+    suppression_proto->set_duration_ms(
+        TimeDeltaToMilliseconds(suppression.duration));
+    suppression_proto->set_recover_goal(suppression.recover_goal);
+  }
+}
+
+void ClientStateFromProto(proto::ClientState* proto,
+                          notifications::ClientState* client_state) {
+  DCHECK(proto->has_type());
+  DCHECK(proto->has_current_max_daily_show());
+  client_state->type = FromSchedulerClientType(proto->type());
+  client_state->current_max_daily_show = proto->current_max_daily_show();
+
+  for (const auto& proto_impression : proto->impressions()) {
+    Impression impression;
+    DCHECK(proto_impression.has_create_time());
+    impression.create_time = MillisecondsToTime(proto_impression.create_time());
+    impression.feedback = FromUserFeedback(proto_impression.feedback());
+    impression.impression = FromImpressionResult(proto_impression.impression());
+    impression.integrated = proto_impression.integrated();
+    impression.task_start_time =
+        FromSchedulerTaskTime(proto_impression.task_start_time());
+    impression.guid = proto_impression.guid();
+    impression.type = client_state->type;
+    client_state->impressions.emplace_back(std::move(impression));
+  }
+
+  if (proto->has_suppression_info()) {
+    const auto& proto_suppression = proto->suppression_info();
+    DCHECK(proto_suppression.has_last_trigger_time());
+    DCHECK(proto_suppression.has_duration_ms());
+    DCHECK(proto_suppression.has_recover_goal());
+
+    SuppressionInfo suppression_info(
+        MillisecondsToTime(proto_suppression.last_trigger_time()),
+        MillisecondsToTimeDelta(proto_suppression.duration_ms()));
+    suppression_info.recover_goal = proto_suppression.recover_goal();
+    client_state->suppression_info = std::move(suppression_info);
+  }
+}
+
+void NotificationEntryToProto(NotificationEntry* entry,
+                              proto::NotificationEntry* proto) {
+  proto->set_type(ToSchedulerClientType(entry->type));
+  proto->set_guid(entry->guid);
+  proto->set_create_time(TimeToMilliseconds(entry->create_time));
+  auto* proto_notification_data = proto->mutable_notification_data();
+  NotificationDataToProto(&entry->notification_data, proto_notification_data);
+  auto* proto_schedule_params = proto->mutable_schedule_params();
+  ScheduleParamsToProto(&entry->schedule_params, proto_schedule_params);
+}
+
+void NotificationEntryFromProto(proto::NotificationEntry* proto,
+                                NotificationEntry* entry) {
+  entry->type = FromSchedulerClientType(proto->type());
+  entry->guid = proto->guid();
+  entry->create_time = MillisecondsToTime(proto->create_time());
+  NotificationDataFromProto(proto->mutable_notification_data(),
+                            &entry->notification_data);
+  ScheduleParamsFromProto(proto->mutable_schedule_params(),
+                          &entry->schedule_params);
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion.h b/chrome/browser/notifications/scheduler/internal/proto_conversion.h
new file mode 100644
index 0000000..382fc4e
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/proto_conversion.h
@@ -0,0 +1,44 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_PROTO_CONVERSION_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_PROTO_CONVERSION_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/notifications/proto/client_state.pb.h"
+#include "chrome/browser/notifications/proto/icon.pb.h"
+#include "chrome/browser/notifications/proto/notification_entry.pb.h"
+#include "chrome/browser/notifications/scheduler/internal/icon_entry.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_types.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+
+namespace notifications {
+
+// Converts an icon entry to icon proto.
+void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto);
+
+// Converts an icon proto to icon entry.
+void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry);
+
+// Converts client state to proto.
+void ClientStateToProto(ClientState* client_state,
+                        notifications::proto::ClientState* proto);
+
+// Converts proto to client state.
+void ClientStateFromProto(proto::ClientState* proto,
+                          notifications::ClientState* client_state);
+
+// Converts notification entry to proto.
+void NotificationEntryToProto(NotificationEntry* entry,
+                              proto::NotificationEntry* proto);
+
+// Converts proto to notification entry.
+void NotificationEntryFromProto(proto::NotificationEntry* proto,
+                                NotificationEntry* entry);
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_PROTO_CONVERSION_H_
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
new file mode 100644
index 0000000..3bf7e31a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
@@ -0,0 +1,189 @@
+// Copyright 2019 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/notifications/scheduler/internal/proto_conversion.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "chrome/browser/notifications/scheduler/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using IconProto = notifications::proto::Icon;
+
+namespace notifications {
+namespace {
+
+const char kUuid[] = "123";
+const char kGuid[] = "testGuid";
+const char kData[] = "bitmapdata";
+
+void TestClientStateConversion(ClientState* client_state) {
+  DCHECK(client_state);
+  notifications::proto::ClientState proto;
+  ClientState expected;
+  ClientStateToProto(client_state, &proto);
+  ClientStateFromProto(&proto, &expected);
+  EXPECT_EQ(*client_state, expected)
+      << " \n Output: \n " << client_state->DebugPrint() << " \n Expected: \n"
+      << expected.DebugPrint();
+}
+
+void TestNotificationEntryConversion(NotificationEntry* entry) {
+  DCHECK(entry);
+  notifications::proto::NotificationEntry proto;
+  NotificationEntry expected(SchedulerClientType::kTest1, "");
+  NotificationEntryToProto(entry, &proto);
+  NotificationEntryFromProto(&proto, &expected);
+  EXPECT_EQ(entry->notification_data, expected.notification_data);
+  EXPECT_EQ(entry->schedule_params, expected.schedule_params);
+
+  EXPECT_EQ(*entry, expected)
+      << "Output: " << test::DebugString(entry)
+      << " \n Expected: " << test::DebugString(&expected);
+}
+
+TEST(ProtoConversionTest, IconEntryFromProto) {
+  IconProto proto;
+  proto.set_uuid(kUuid);
+  proto.set_icon(kData);
+  IconEntry entry;
+
+  IconEntryFromProto(&proto, &entry);
+
+  // Verify entry data.
+  EXPECT_EQ(entry.uuid, kUuid);
+  EXPECT_EQ(entry.data, kData);
+}
+
+TEST(ProtoConversionTest, IconEntryToProto) {
+  IconEntry entry;
+  entry.data = kData;
+  entry.uuid = kUuid;
+  IconProto proto;
+
+  IconEntryToProto(&entry, &proto);
+
+  // Verify proto data.
+  EXPECT_EQ(proto.icon(), kData);
+  EXPECT_EQ(proto.uuid(), kUuid);
+}
+
+// Verifies client state proto conversion.
+TEST(ProtoConversionTest, ClientStateProtoConversion) {
+  // Verify basic fields.
+  ClientState client_state;
+  test::ImpressionTestData test_data{
+      SchedulerClientType::kTest1, 3, {}, base::nullopt};
+  test::AddImpressionTestData(test_data, &client_state);
+  TestClientStateConversion(&client_state);
+
+  // Verify suppression info.
+  base::Time last_trigger_time;
+  bool success =
+      base::Time::FromString("04/25/20 01:00:00 AM", &last_trigger_time);
+  DCHECK(success);
+  auto duration = base::TimeDelta::FromDays(7);
+  auto suppression = SuppressionInfo(last_trigger_time, duration);
+  suppression.recover_goal = 5;
+  client_state.suppression_info = std::move(suppression);
+  TestClientStateConversion(&client_state);
+}
+
+// Verifies impression proto conversion.
+TEST(ProtoConversionTest, ImpressionProtoConversion) {
+  ClientState client_state;
+  client_state.type = SchedulerClientType::kTest1;
+  base::Time create_time;
+  bool success = base::Time::FromString("03/25/19 00:00:00 AM", &create_time);
+  DCHECK(success);
+  Impression impression{create_time,
+                        UserFeedback::kHelpful,
+                        ImpressionResult::kPositive,
+                        true /*integrated*/,
+                        SchedulerTaskTime::kMorning,
+                        kGuid,
+                        SchedulerClientType::kTest1};
+  client_state.impressions.emplace_back(impression);
+  TestClientStateConversion(&client_state);
+
+  auto& first_impression = *client_state.impressions.begin();
+
+  // Verify all feedback types.
+  std::vector<UserFeedback> feedback_types{
+      UserFeedback::kNoFeedback, UserFeedback::kHelpful,
+      UserFeedback::kNotHelpful, UserFeedback::kClick,
+      UserFeedback::kDismiss,    UserFeedback::kIgnore};
+  for (const auto feedback_type : feedback_types) {
+    first_impression.feedback = feedback_type;
+    TestClientStateConversion(&client_state);
+  }
+
+  // Verify all impression result types.
+  std::vector<ImpressionResult> impression_results{
+      ImpressionResult::kInvalid, ImpressionResult::kPositive,
+      ImpressionResult::kNegative, ImpressionResult::kNeutral};
+  for (const auto impression_result : impression_results) {
+    first_impression.impression = impression_result;
+    TestClientStateConversion(&client_state);
+  }
+
+  // Verify all scheduler task time types.
+  std::vector<SchedulerTaskTime> task_times{SchedulerTaskTime::kUnknown,
+                                            SchedulerTaskTime::kMorning,
+                                            SchedulerTaskTime::kEvening};
+  for (const auto task_start_time : task_times) {
+    first_impression.task_start_time = task_start_time;
+    TestClientStateConversion(&client_state);
+  }
+}
+
+// Verifies multiple impressions are serialized correctly.
+TEST(ProtoConversionTest, MultipleImpressionConversion) {
+  ClientState client_state;
+  base::Time create_time;
+  bool success = base::Time::FromString("04/25/20 01:00:00 AM", &create_time);
+  DCHECK(success);
+
+  Impression impression{create_time, UserFeedback::kHelpful,
+                        ImpressionResult::kPositive, true,
+                        SchedulerTaskTime::kMorning};
+  Impression other_impression{create_time, UserFeedback::kNoFeedback,
+                              ImpressionResult::kNegative, false,
+                              SchedulerTaskTime::kEvening};
+  client_state.impressions.emplace_back(std::move(impression));
+  client_state.impressions.emplace_back(std::move(other_impression));
+  TestClientStateConversion(&client_state);
+}
+
+// Verifies notification entry proto conversion.
+TEST(ProtoConversionTest, NotificationEntryConversion) {
+  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
+  bool success =
+      base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time);
+  DCHECK(success);
+  TestNotificationEntryConversion(&entry);
+
+  // Test notification data.
+  entry.notification_data.id = kGuid;
+  entry.notification_data.title = "title";
+  entry.notification_data.message = "message";
+  entry.notification_data.icon_uuid = "icon_uuid";
+  entry.notification_data.url = "url";
+  TestNotificationEntryConversion(&entry);
+
+  // Test scheduling params.
+  const ScheduleParams::Priority priorities[] = {
+      ScheduleParams::Priority::kLow, ScheduleParams::Priority::kHigh,
+      ScheduleParams::Priority::kNoThrottle};
+  for (auto priority : priorities) {
+    entry.schedule_params.priority = priority;
+    TestNotificationEntryConversion(&entry);
+  }
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
new file mode 100644
index 0000000..bae20ee6
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
@@ -0,0 +1,138 @@
+// Copyright 2019 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/notifications/scheduler/internal/scheduled_notification_manager.h"
+
+#include <algorithm>
+#include <map>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+#include "chrome/browser/notifications/scheduler/public/notification_params.h"
+
+namespace notifications {
+namespace {
+
+// Comparator used to sort notification entries based on creation time.
+bool CreateTimeCompare(const NotificationEntry* lhs,
+                       const NotificationEntry* rhs) {
+  DCHECK(lhs && rhs);
+  return lhs->create_time <= rhs->create_time;
+}
+
+class ScheduledNotificationManagerImpl : public ScheduledNotificationManager {
+ public:
+  using Store = std::unique_ptr<CollectionStore<NotificationEntry>>;
+
+  ScheduledNotificationManagerImpl(Store store)
+      : store_(std::move(store)), delegate_(nullptr), weak_ptr_factory_(this) {}
+
+ private:
+  void Init(Delegate* delegate, InitCallback callback) override {
+    DCHECK(!delegate_);
+    delegate_ = delegate;
+    store_->InitAndLoad(
+        base::BindOnce(&ScheduledNotificationManagerImpl::OnStoreInitialized,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  // NotificationManager implementation.
+  void ScheduleNotification(
+      std::unique_ptr<NotificationParams> notification_params) override {
+    DCHECK(notification_params);
+    std::string guid = notification_params->guid;
+    DCHECK(!guid.empty());
+    if (notifications_.find(guid) != notifications_.end()) {
+      // TODO(xingliu): Report duplicate guid failure.
+      return;
+    }
+
+    auto entry =
+        std::make_unique<NotificationEntry>(notification_params->type, guid);
+    entry->notification_data =
+        std::move(notification_params->notification_data);
+    entry->schedule_params = std::move(notification_params->schedule_params);
+    auto* entry_ptr = entry.get();
+    notifications_.emplace(guid, std::move(entry));
+    store_->Add(
+        guid, *entry_ptr,
+        base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationAdded,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void DisplayNotification(const std::string& guid) override {
+    auto it = notifications_.find(guid);
+    if (it == notifications_.end())
+      return;
+
+    // Move the entry to delegate, and delete it from the storage.
+    auto notification_entry = std::move(it->second);
+    notifications_.erase(guid);
+    store_->Delete(
+        guid,
+        base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationDeleted,
+                       weak_ptr_factory_.GetWeakPtr()));
+    if (delegate_)
+      delegate_->DisplayNotification(std::move(notification_entry));
+  }
+
+  void GetAllNotifications(Notifications* notifications) override {
+    DCHECK(notifications);
+
+    for (const auto& pair : notifications_) {
+      const auto& notif = pair.second;
+      DCHECK(notif);
+      (*notifications)[notif->type].emplace_back(notif.get());
+    }
+
+    // Sort by creation time for each notification type.
+    for (auto it = notifications->begin(); it != notifications->end(); ++it) {
+      std::sort(it->second.begin(), it->second.end(), &CreateTimeCompare);
+    }
+  }
+
+  void OnStoreInitialized(InitCallback callback,
+                          bool success,
+                          CollectionStore<NotificationEntry>::Entries entries) {
+    if (!success) {
+      std::move(callback).Run(false);
+      return;
+    }
+
+    for (auto it = entries.begin(); it != entries.end(); ++it) {
+      std::string guid = (*it)->guid;
+      notifications_.emplace(guid, std::move(*it));
+    }
+
+    std::move(callback).Run(true);
+  }
+
+  void OnNotificationAdded(bool success) { NOTIMPLEMENTED(); }
+
+  void OnNotificationDeleted(bool success) { NOTIMPLEMENTED(); }
+
+  Store store_;
+  Delegate* delegate_;
+  std::map<std::string, std::unique_ptr<NotificationEntry>> notifications_;
+
+  base::WeakPtrFactory<ScheduledNotificationManagerImpl> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerImpl);
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<ScheduledNotificationManager>
+ScheduledNotificationManager::Create(
+    std::unique_ptr<CollectionStore<NotificationEntry>> store) {
+  return std::make_unique<ScheduledNotificationManagerImpl>(std::move(store));
+}
+
+ScheduledNotificationManager::ScheduledNotificationManager() = default;
+
+ScheduledNotificationManager::~ScheduledNotificationManager() = default;
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h
new file mode 100644
index 0000000..6ed3e8bc
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h
@@ -0,0 +1,73 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULED_NOTIFICATION_MANAGER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULED_NOTIFICATION_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/internal/collection_store.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace notifications {
+
+struct NotificationEntry;
+struct NotificationParams;
+
+// Class to manage in-memory scheduled notifications loaded from the storage.
+class ScheduledNotificationManager {
+ public:
+  using InitCallback = base::OnceCallback<void(bool)>;
+  using Notifications =
+      std::map<SchedulerClientType, std::vector<const NotificationEntry*>>;
+
+  // Delegate that receives events from the manager.
+  class Delegate {
+   public:
+    // Displays a notification to the user.
+    virtual void DisplayNotification(
+        std::unique_ptr<NotificationEntry> notification) = 0;
+
+    Delegate() = default;
+    virtual ~Delegate() = default;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Delegate);
+  };
+
+  // Creates the instance.
+  static std::unique_ptr<ScheduledNotificationManager> Create(
+      std::unique_ptr<CollectionStore<NotificationEntry>> store);
+
+  // Initializes the notification store.
+  virtual void Init(Delegate* delegate, InitCallback callback) = 0;
+
+  // Adds a new notification.
+  virtual void ScheduleNotification(
+      std::unique_ptr<NotificationParams> notification_params) = 0;
+
+  // Displays a notification, the scheduled notification will be removed from
+  // storage, then Delegate::DisplayNotification() should be invoked.
+  virtual void DisplayNotification(const std::string& guid) = 0;
+
+  // Gets all scheduled notifications. For each type, notifications are sorted
+  // by creation timestamp.
+  virtual void GetAllNotifications(Notifications* notifications) = 0;
+
+  virtual ~ScheduledNotificationManager();
+
+ protected:
+  ScheduledNotificationManager();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManager);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULED_NOTIFICATION_MANAGER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
new file mode 100644
index 0000000..b5b1c132
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
@@ -0,0 +1,229 @@
+// Copyright 2019 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/notifications/scheduler/internal/scheduled_notification_manager.h"
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/notifications/scheduler/internal/collection_store.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+#include "chrome/browser/notifications/scheduler/public/notification_params.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using ::testing::Invoke;
+using Entries = std::vector<std::unique_ptr<notifications::NotificationEntry>>;
+
+namespace notifications {
+namespace {
+
+const char kGuid[] = "test_guid_1234";
+const char kTitle[] = "test_title";
+
+NotificationEntry CreateNotificationEntry() {
+  return NotificationEntry(SchedulerClientType::kUnknown, base::GenerateGUID());
+}
+
+class MockDelegate : public ScheduledNotificationManager::Delegate {
+ public:
+  MockDelegate() = default;
+  MOCK_METHOD1(DisplayNotification, void(std::unique_ptr<NotificationEntry>));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDelegate);
+};
+
+class MockNotificationStore : public CollectionStore<NotificationEntry> {
+ public:
+  MockNotificationStore() {}
+
+  MOCK_METHOD1(InitAndLoad,
+               void(CollectionStore<NotificationEntry>::LoadCallback));
+  MOCK_METHOD3(Add,
+               void(const std::string&,
+                    const NotificationEntry&,
+                    base::OnceCallback<void(bool)>));
+  MOCK_METHOD3(Update,
+               void(const std::string&,
+                    const NotificationEntry&,
+                    base::OnceCallback<void(bool)>));
+  MOCK_METHOD2(Delete,
+               void(const std::string&, base::OnceCallback<void(bool)>));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockNotificationStore);
+};
+
+class ScheduledNotificationManagerTest : public testing::Test {
+ public:
+  ScheduledNotificationManagerTest() : store_(nullptr) {}
+  ~ScheduledNotificationManagerTest() override = default;
+
+  void SetUp() override {
+    delegate_ = std::make_unique<MockDelegate>();
+    auto store = std::make_unique<MockNotificationStore>();
+    store_ = store.get();
+    manager_ = ScheduledNotificationManager::Create(std::move(store));
+  }
+
+ protected:
+  ScheduledNotificationManager* manager() { return manager_.get(); }
+  MockNotificationStore* store() { return store_; }
+  MockDelegate* delegate() { return delegate_.get(); }
+
+  // Initializes the manager with predefined data in the store.
+  void InitWithData(std::vector<NotificationEntry> data) {
+    Entries entries;
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      auto entry_ptr = std::make_unique<NotificationEntry>(
+          SchedulerClientType::kUnknown, it->guid);
+      *(entry_ptr.get()) = *it;
+      entries.emplace_back(std::move(entry_ptr));
+    }
+
+    // Initialize the store and call the callback.
+    EXPECT_CALL(*store(), InitAndLoad(_))
+        .WillOnce(
+            Invoke([&entries](base::OnceCallback<void(bool, Entries)> cb) {
+              std::move(cb).Run(true, std::move(entries));
+            }));
+    base::RunLoop loop;
+    manager()->Init(delegate(),
+                    base::BindOnce(
+                        [](base::RepeatingClosure closure, bool success) {
+                          EXPECT_TRUE(success);
+                          std::move(closure).Run();
+                        },
+                        loop.QuitClosure()));
+    loop.Run();
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<MockDelegate> delegate_;
+  MockNotificationStore* store_;
+  std::unique_ptr<ScheduledNotificationManager> manager_;
+  DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerTest);
+};
+
+// Verify that error is received when initialization failed.
+TEST_F(ScheduledNotificationManagerTest, InitFailed) {
+  EXPECT_CALL(*store(), InitAndLoad(_))
+      .WillOnce(Invoke([](base::OnceCallback<void(bool, Entries)> cb) {
+        std::move(cb).Run(false, Entries());
+      }));
+
+  base::RunLoop loop;
+  manager()->Init(delegate(),
+                  base::BindOnce(
+                      [](base::RepeatingClosure closure, bool success) {
+                        // Expected to receive error.
+                        EXPECT_FALSE(success);
+                        std::move(closure).Run();
+                      },
+                      loop.QuitClosure()));
+  loop.Run();
+}
+
+// Test to schedule a notification.
+TEST_F(ScheduledNotificationManagerTest, ScheduleNotification) {
+  InitWithData(std::vector<NotificationEntry>());
+  NotificationData notification_data;
+  notification_data.title = kTitle;
+  ScheduleParams schedule_params;
+  schedule_params.priority = ScheduleParams::Priority::kHigh;
+  auto params = std::make_unique<NotificationParams>(
+      SchedulerClientType::kUnknown, notification_data, schedule_params);
+  std::string guid = params->guid;
+  EXPECT_FALSE(guid.empty());
+
+  // Verify call contract.
+  EXPECT_CALL(*store(), Add(guid, _, _));
+  manager()->ScheduleNotification(std::move(params));
+
+  // Verify in-memory data.
+  ScheduledNotificationManager::Notifications notifications;
+  manager()->GetAllNotifications(&notifications);
+  EXPECT_EQ(notifications.size(), 1u);
+  const NotificationEntry* entry = *(notifications.begin()->second.begin());
+  EXPECT_EQ(entry->guid, guid);
+  EXPECT_NE(entry->create_time, base::Time());
+
+  // TODO(xingliu): change these to compare with operator==.
+  EXPECT_EQ(entry->notification_data.title, kTitle);
+  EXPECT_EQ(entry->schedule_params.priority, ScheduleParams::Priority::kHigh);
+}
+
+// Test to schedule a notification without guid, we will auto generated one.
+TEST_F(ScheduledNotificationManagerTest, ScheduleNotificationEmptyGuid) {
+  InitWithData(std::vector<NotificationEntry>());
+  auto params = std::make_unique<NotificationParams>(
+      SchedulerClientType::kUnknown, NotificationData(), ScheduleParams());
+
+  // Verify call contract.
+  EXPECT_CALL(*store(), Add(_, _, _));
+  manager()->ScheduleNotification(std::move(params));
+
+  // Verify in-memory data.
+  ScheduledNotificationManager::Notifications notifications;
+  manager()->GetAllNotifications(&notifications);
+  EXPECT_EQ(notifications.size(), 1u);
+  const NotificationEntry* entry = *(notifications.begin()->second.begin());
+  EXPECT_NE(entry->guid, std::string());
+  EXPECT_NE(entry->create_time, base::Time());
+}
+
+// TODO(xingliu): change this to compare with operator==.
+MATCHER_P(NotificationEntryIs, expected, "") {
+  return arg->guid == expected.guid;
+}
+
+// Test to display a notification.
+TEST_F(ScheduledNotificationManagerTest, DisplayNotification) {
+  auto entry = CreateNotificationEntry();
+  entry.guid = kGuid;
+  InitWithData(std::vector<NotificationEntry>({entry}));
+
+  // Verify delegate and dependency call contract.
+  EXPECT_CALL(*store(), Delete(kGuid, _));
+  EXPECT_CALL(*delegate(), DisplayNotification(NotificationEntryIs(entry)));
+  manager()->DisplayNotification(kGuid);
+
+  // Verify in-memory data.
+  ScheduledNotificationManager::Notifications notifications;
+  manager()->GetAllNotifications(&notifications);
+  EXPECT_TRUE(notifications.empty());
+}
+
+// Verify GetAllNotifications API, the notification should be sorted based on
+// creation timestamp.
+TEST_F(ScheduledNotificationManagerTest, GetAllNotifications) {
+  // Ordering: entry1.create_time < entry0.create_time < entry2.create_time.
+  auto now = base::Time::Now();
+  auto entry0 = CreateNotificationEntry();
+  entry0.create_time = now;
+  auto entry1 = CreateNotificationEntry();
+  entry1.create_time = now - base::TimeDelta::FromMinutes(1);
+  auto entry2 = CreateNotificationEntry();
+  entry2.create_time = now + base::TimeDelta::FromMinutes(1);
+
+  InitWithData(std::vector<NotificationEntry>({entry0, entry1, entry2}));
+  ScheduledNotificationManager::Notifications notifications;
+  manager()->GetAllNotifications(&notifications);
+  EXPECT_EQ(notifications.size(), 1u);
+  EXPECT_EQ(notifications.begin()->second.size(), 3u);
+
+  // Entries returned by GetAllNotifications() should be sorted by creation
+  // timestamp.
+  const NotificationEntry* output0 = notifications.begin()->second.front();
+  const NotificationEntry* output2 = notifications.begin()->second.back();
+  EXPECT_EQ(output0->create_time, entry1.create_time);
+  EXPECT_EQ(output2->create_time, entry2.create_time);
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_config.cc b/chrome/browser/notifications/scheduler/internal/scheduler_config.cc
new file mode 100644
index 0000000..b9a93d8
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_config.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 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/notifications/scheduler/internal/scheduler_config.h"
+
+namespace notifications {
+
+// The impression history is hold for 4 weeks.
+constexpr base::TimeDelta kDefaultImpressionExpiration =
+    base::TimeDelta::FromDays(28);
+
+// The suppression lasts 8 weeks.
+constexpr base::TimeDelta kDefaultSuppressionDuration =
+    base::TimeDelta::FromDays(56);
+
+// The morning task by default will run at 7am.
+constexpr int kDefaultMorningTaskHour = 7;
+
+// The evening task by default will run at 6pm.
+constexpr int kDefaultEveningTaskHour = 18;
+
+// Check consecutive notification dismisses in this duration to generate a
+// dismiss event.
+constexpr base::TimeDelta kDefaultDimissDuration = base::TimeDelta::FromDays(7);
+
+// Default background task time window duration.
+constexpr base::TimeDelta kDefaultBackgroundTaskWindowDuration =
+    base::TimeDelta::FromHours(1);
+
+// static
+std::unique_ptr<SchedulerConfig> SchedulerConfig::Create() {
+  return std::make_unique<SchedulerConfig>();
+}
+
+SchedulerConfig::SchedulerConfig()
+    : max_daily_shown_all_type(3),
+      max_daily_shown_per_type(10),
+      impression_expiration(kDefaultImpressionExpiration),
+      suppression_duration(kDefaultSuppressionDuration),
+      dismiss_count(3),
+      dismiss_duration(kDefaultDimissDuration),
+      morning_task_hour(kDefaultMorningTaskHour),
+      evening_task_hour(kDefaultEveningTaskHour),
+      background_task_window_duration(kDefaultBackgroundTaskWindowDuration) {
+  // TODO(xingliu): Add constructor using finch data.
+}
+
+SchedulerConfig::~SchedulerConfig() = default;
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_config.h b/chrome/browser/notifications/scheduler/internal/scheduler_config.h
new file mode 100644
index 0000000..617983e
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_config.h
@@ -0,0 +1,61 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_CONFIG_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_CONFIG_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+
+namespace notifications {
+
+// Configuration of notification scheduler system.
+struct SchedulerConfig {
+  // Creates a default scheduler config.
+  static std::unique_ptr<SchedulerConfig> Create();
+
+  SchedulerConfig();
+  ~SchedulerConfig();
+
+  // Maximum number of all types of notifications shown to the user per day.
+  int max_daily_shown_all_type;
+
+  // Maximum number of notifications shown to the user per day for each type.
+  int max_daily_shown_per_type;
+
+  // The time for a notification impression history data to expire. The
+  // impression history will be deleted then.
+  base::TimeDelta impression_expiration;
+
+  // Duration of suppression when negative impression is applied.
+  base::TimeDelta suppression_duration;
+
+  // The number of consecutive notification dismisses to generate a dismiss
+  // event.
+  int dismiss_count;
+
+  // Used to check whether |dismiss_count| consecutive notification dimisses are
+  // in this duration, to generate a dismiss event.
+  base::TimeDelta dismiss_duration;
+
+  // The hour (from 0 to 23) to run the morning background task for notification
+  // scheduler.
+  int morning_task_hour;
+
+  // The hour (from 0 to 23) to run the evening background task for notification
+  // scheduler.
+  int evening_task_hour;
+
+  // The time window to launch the background task.
+  base::TimeDelta background_task_window_duration;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SchedulerConfig);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_CONFIG_H_
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc b/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
new file mode 100644
index 0000000..ad7f8dd5
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
@@ -0,0 +1,61 @@
+// Copyright 2019 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/notifications/scheduler/internal/scheduler_utils.h"
+
+#include "chrome/browser/notifications/scheduler/internal/impression_types.h"
+
+namespace notifications {
+
+bool ToLocalHour(int hour,
+                 const base::Time& today,
+                 int day_delta,
+                 base::Time* out) {
+  DCHECK_GE(hour, 0);
+  DCHECK_LE(hour, 23);
+  DCHECK(out);
+
+  // Gets the local time at |hour| in yesterday.
+  base::Time another_day = today + base::TimeDelta::FromDays(day_delta);
+  base::Time::Exploded another_day_exploded;
+  another_day.LocalExplode(&another_day_exploded);
+  another_day_exploded.hour = hour;
+  another_day_exploded.minute = 0;
+  another_day_exploded.second = 0;
+  another_day_exploded.millisecond = 0;
+
+  // Converts local exploded time to time stamp.
+  return base::Time::FromLocalExploded(another_day_exploded, out);
+}
+
+void NotificationsShownToday(
+    const std::map<SchedulerClientType, const ClientState*>& client_states,
+    std::map<SchedulerClientType, int>* shown_per_type,
+    int* shown_total,
+    SchedulerClientType* last_shown_type) {
+  base::Time last_shown_time;
+  base::Time now(base::Time::Now());
+  base::Time beginning_of_today;
+  bool success = ToLocalHour(0, now, 0, &beginning_of_today);
+  DCHECK(success);
+
+  for (const auto& state : client_states) {
+    const auto* client_state = state.second;
+    for (const auto& impression : client_state->impressions) {
+      // Tracks last notification shown to the user.
+      if (impression.create_time > last_shown_time) {
+        last_shown_time = impression.create_time;
+        *last_shown_type = client_state->type;
+      }
+
+      // Count notification shown today.
+      if (impression.create_time >= beginning_of_today) {
+        (*shown_per_type)[client_state->type]++;
+        ++(*shown_total);
+      }
+    }
+  }
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils.h b/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
new file mode 100644
index 0000000..832a749
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
@@ -0,0 +1,37 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_UTILS_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_UTILS_H_
+
+#include <map>
+
+#include "base/time/time.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace notifications {
+
+struct ClientState;
+
+// Retrieves the time stamp of a certain hour at a certain day from today.
+// |hour| must be in the range of [0, 23].
+// |today| is a timestamp to define today, usually caller can directly pass in
+// the current system time.
+// |day_delta| is the different between the output date and today.
+// Returns false if the conversion is failed.
+bool ToLocalHour(int hour,
+                 const base::Time& today,
+                 int day_delta,
+                 base::Time* out);
+
+// Calculates the notifications shown today from impression data.
+void NotificationsShownToday(
+    const std::map<SchedulerClientType, const ClientState*>& client_states,
+    std::map<SchedulerClientType, int>* shown_per_type,
+    int* shown_total,
+    SchedulerClientType* last_shown_type);
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_UTILS_H_
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc b/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc
new file mode 100644
index 0000000..3c28e3a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2019 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/notifications/scheduler/internal/scheduler_utils.h"
+
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace notifications {
+namespace {
+
+// Verifies we can get the correct time stamp at certain hour in yesterday.
+TEST(SchedulerUtilsTest, ToLocalHour) {
+  base::Time today, another_day, expected;
+
+  // Timestamp of another day in the past.
+  EXPECT_TRUE(base::Time::FromString("10/15/07 12:45:12 PM", &today));
+  EXPECT_TRUE(ToLocalHour(6, today, -1, &another_day));
+  EXPECT_TRUE(base::Time::FromString("10/14/07 06:00:00 AM", &expected));
+  EXPECT_EQ(expected, another_day);
+
+  EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &today));
+  EXPECT_TRUE(ToLocalHour(0, today, -1, &another_day));
+  EXPECT_TRUE(base::Time::FromString("03/24/19 00:00:00 AM", &expected));
+  EXPECT_EQ(expected, another_day);
+
+  // Timestamp of the same day.
+  EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &today));
+  EXPECT_TRUE(ToLocalHour(0, today, 0, &another_day));
+  EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &expected));
+  EXPECT_EQ(expected, another_day);
+
+  // Timestamp of another day in the future.
+  EXPECT_TRUE(base::Time::FromString("03/25/19 06:35:27 AM", &today));
+  EXPECT_TRUE(ToLocalHour(16, today, 7, &another_day));
+  EXPECT_TRUE(base::Time::FromString("04/01/19 16:00:00 PM", &expected));
+  EXPECT_EQ(expected, another_day);
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h b/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h
deleted file mode 100644
index 3c33cce5..0000000
--- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace notifications {
-
-// Interface to schedule a background task on platform OS to run the
-// notification scheduler job.
-class NotificationBackgroundTaskScheduler {
- public:
-  // Interface used to handle background task events.
-  class Handler {
-   public:
-    // Called when the background task is started.
-    virtual void OnStartTask() = 0;
-
-    // Called when the background task is stopped by the OS when it wants to
-    // reallocate resources, our task is not finished yet in this case. The
-    // handler implementation should explicitly decide whether the task should
-    // be rescheduled and run later.
-    virtual void OnStopTask() = 0;
-
-   protected:
-    Handler() = default;
-    virtual ~Handler() = default;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Handler);
-  };
-
-  // Schedules a background task in a time window between |window_start| and
-  // |window_end|. This will update the current background task. Only one
-  // background task exists for notification scheduler. |scheduler_task_time|
-  // tag is passed to background task go support arbitrary time background task.
-  virtual void Schedule(notifications::SchedulerTaskTime scheduler_task_time,
-                        base::TimeDelta window_start,
-                        base::TimeDelta window_end) = 0;
-
-  // Cancels the background task.
-  virtual void Cancel() = 0;
-
-  virtual ~NotificationBackgroundTaskScheduler() = default;
-
- protected:
-  NotificationBackgroundTaskScheduler() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NotificationBackgroundTaskScheduler);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h
index 94d7322..1586117c 100644
--- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h
+++ b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
+#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
 
 // This class contains:
 // 1. Android implementation of NotificationBackgroundTaskScheduler, which
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h
index 13584e2..89a4e99 100644
--- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h
+++ b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
+#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
 
 // Default implementation of NotificatioNBackgroundTaskScheduler.
 class NotificationBackgroundTaskSchedulerImpl
diff --git a/chrome/browser/notifications/scheduler/notification_data.cc b/chrome/browser/notifications/scheduler/notification_data.cc
deleted file mode 100644
index 8e2e019..0000000
--- a/chrome/browser/notifications/scheduler/notification_data.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_data.h"
-
-namespace notifications {
-
-NotificationData::NotificationData() = default;
-
-NotificationData::NotificationData(const NotificationData& other) = default;
-
-bool NotificationData::operator==(const NotificationData& other) const {
-  return id == other.id && title == other.title && message == other.message &&
-         icon_uuid == other.icon_uuid && url == other.url;
-}
-
-NotificationData::~NotificationData() = default;
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_data.h b/chrome/browser/notifications/scheduler/notification_data.h
deleted file mode 100644
index 2754899..0000000
--- a/chrome/browser/notifications/scheduler/notification_data.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_DATA_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_DATA_H_
-
-#include <string>
-
-namespace notifications {
-
-// Contains data used to display a scheduled notification. All fields will be
-// persisted to disk as protobuffer NotificationData. The clients of
-// notification scheduler can optionally use the texts or icon in this struct,
-// or use hard coded assets id.
-struct NotificationData {
-  NotificationData();
-  NotificationData(const NotificationData& other);
-  bool operator==(const NotificationData& other) const;
-  ~NotificationData();
-
-  // The unique identifier of the notification. This is not used as the key for
-  // the database entry, but the id of other notification struct plumbed to
-  // the scheduler system.
-  std::string id;
-
-  // The title of the notification.
-  std::string title;
-
-  // The body text of the notification.
-  std::string message;
-
-  // The unique identifier of the icon, which must be loaded asynchronously into
-  // memory.
-  std::string icon_uuid;
-
-  // URL of the web site responsible for showing the notification.
-  std::string url;
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_DATA_H_
diff --git a/chrome/browser/notifications/scheduler/notification_entry.cc b/chrome/browser/notifications/scheduler/notification_entry.cc
deleted file mode 100644
index e1e7d31e..0000000
--- a/chrome/browser/notifications/scheduler/notification_entry.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_entry.h"
-
-#include <utility>
-
-namespace notifications {
-
-NotificationEntry::NotificationEntry()
-    : NotificationEntry(SchedulerClientType::kUnknown, std::string()) {}
-
-NotificationEntry::NotificationEntry(SchedulerClientType type,
-                                     const std::string& guid)
-    : type(type), guid(guid), create_time(base::Time::Now()) {}
-
-NotificationEntry::NotificationEntry(const NotificationEntry& other) = default;
-
-bool NotificationEntry::operator==(const NotificationEntry& other) const {
-  return type == other.type && guid == other.guid &&
-         create_time == other.create_time &&
-         notification_data == other.notification_data &&
-         schedule_params == other.schedule_params;
-}
-
-NotificationEntry::~NotificationEntry() = default;
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_entry.h b/chrome/browser/notifications/scheduler/notification_entry.h
deleted file mode 100644
index 1ee0c7e2..0000000
--- a/chrome/browser/notifications/scheduler/notification_entry.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_
-
-#include <string>
-
-#include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/notification_data.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-#include "chrome/browser/notifications/scheduler/schedule_params.h"
-
-namespace notifications {
-
-// Represents the in-memory counterpart of scheduled notification database
-// record.
-struct NotificationEntry {
-  NotificationEntry();
-  NotificationEntry(SchedulerClientType type, const std::string& guid);
-  NotificationEntry(const NotificationEntry& other);
-  bool operator==(const NotificationEntry& other) const;
-  ~NotificationEntry();
-
-  // The type of the notification.
-  SchedulerClientType type;
-
-  // The unique id of the notification database entry.
-  std::string guid;
-
-  // Creation timestamp.
-  base::Time create_time;
-
-  // Contains information to construct the notification.
-  NotificationData notification_data;
-
-  // Scheduling details.
-  ScheduleParams schedule_params;
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/notification_params.cc b/chrome/browser/notifications/scheduler/notification_params.cc
deleted file mode 100644
index 8cf8ab81..0000000
--- a/chrome/browser/notifications/scheduler/notification_params.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_params.h"
-
-#include <utility>
-
-#include "base/guid.h"
-#include "chrome/browser/notifications/scheduler/schedule_params.h"
-
-namespace notifications {
-
-NotificationParams::NotificationParams(SchedulerClientType type,
-                                       NotificationData notification_data,
-                                       ScheduleParams schedule_params)
-    : type(type),
-      guid(base::GenerateGUID()),
-      notification_data(std::move(notification_data)),
-      schedule_params(std::move(schedule_params)) {}
-
-NotificationParams::~NotificationParams() = default;
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_params.h b/chrome/browser/notifications/scheduler/notification_params.h
deleted file mode 100644
index 6bfabf7..0000000
--- a/chrome/browser/notifications/scheduler/notification_params.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_PARAMS_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_PARAMS_H_
-
-#include <memory>
-
-#include "chrome/browser/notifications/scheduler/notification_data.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-#include "chrome/browser/notifications/scheduler/schedule_params.h"
-
-namespace notifications {
-
-// Struct used to schedule a notification.
-struct NotificationParams {
-  NotificationParams(SchedulerClientType type,
-                     NotificationData notification,
-                     ScheduleParams schedule_params);
-  ~NotificationParams();
-
-  // The type of notification using the scheduling system.
-  SchedulerClientType type;
-
-  // An auto generated unique id of the scheduled notification.
-  std::string guid;
-
-  // Data used to show the notification, such as text or title on the
-  // notification.
-  NotificationData notification_data;
-
-  // Scheduling details used to determine when to show the notification.
-  ScheduleParams schedule_params;
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_PARAMS_H_
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service.h b/chrome/browser/notifications/scheduler/notification_schedule_service.h
deleted file mode 100644
index 1e6091f..0000000
--- a/chrome/browser/notifications/scheduler/notification_schedule_service.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/keyed_service/core/keyed_service.h"
-
-namespace notifications {
-
-struct NotificationParams;
-
-// Service to schedule a notification to display in the future. An internal
-// throttling mechanism will be applied to limit the maximum notification shown
-// to the user. Also user's interaction with the notification will affect the
-// frequency of notification delivery.
-class NotificationScheduleService : public KeyedService {
- public:
-  // Schedules a notification to display.
-  virtual void Schedule(
-      std::unique_ptr<NotificationParams> notification_params) = 0;
-
- protected:
-  NotificationScheduleService() = default;
-  ~NotificationScheduleService() override = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NotificationScheduleService);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_H_
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc b/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc
index b884da0..899d980852 100644
--- a/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc
+++ b/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc
@@ -8,9 +8,8 @@
 #include <utility>
 
 #include "chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h"
-#include "chrome/browser/notifications/scheduler/notification_schedule_service.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_context.h"
+#include "chrome/browser/notifications/scheduler/public/notification_schedule_service.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h"
 #include "chrome/browser/notifications/scheduler/schedule_service_factory_helper.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service_impl.cc b/chrome/browser/notifications/scheduler/notification_schedule_service_impl.cc
deleted file mode 100644
index f9565982..0000000
--- a/chrome/browser/notifications/scheduler/notification_schedule_service_impl.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_schedule_service_impl.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "chrome/browser/notifications/scheduler/notification_params.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler.h"
-
-namespace notifications {
-
-NotificationScheduleServiceImpl::NotificationScheduleServiceImpl(
-    std::unique_ptr<NotificationScheduler> scheduler)
-    : scheduler_(std::move(scheduler)) {}
-
-NotificationScheduleServiceImpl::~NotificationScheduleServiceImpl() = default;
-
-void NotificationScheduleServiceImpl::Schedule(
-    std::unique_ptr<NotificationParams> notification_params) {
-  scheduler_->Schedule(std::move(notification_params));
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service_impl.h b/chrome/browser/notifications/scheduler/notification_schedule_service_impl.h
deleted file mode 100644
index d739bf4..0000000
--- a/chrome/browser/notifications/scheduler/notification_schedule_service_impl.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/notification_schedule_service.h"
-
-namespace notifications {
-
-class NotificationScheduler;
-struct NotificationParams;
-
-class NotificationScheduleServiceImpl : public NotificationScheduleService {
- public:
-  explicit NotificationScheduleServiceImpl(
-      std::unique_ptr<NotificationScheduler> scheduler);
-  ~NotificationScheduleServiceImpl() override;
-
- private:
-  // NotificationScheduleService implementation.
-  void Schedule(
-      std::unique_ptr<NotificationParams> notification_params) override;
-
-  // Provides the actual notification scheduling functionalities.
-  std::unique_ptr<NotificationScheduler> scheduler_;
-
-  DISALLOW_COPY_AND_ASSIGN(NotificationScheduleServiceImpl);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler.cc b/chrome/browser/notifications/scheduler/notification_scheduler.cc
deleted file mode 100644
index 4c84d8bd..0000000
--- a/chrome/browser/notifications/scheduler/notification_scheduler.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_scheduler.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "chrome/browser/notifications/scheduler/display_decider.h"
-#include "chrome/browser/notifications/scheduler/distribution_policy.h"
-#include "chrome/browser/notifications/scheduler/icon_store.h"
-#include "chrome/browser/notifications/scheduler/impression_history_tracker.h"
-#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
-#include "chrome/browser/notifications/scheduler/notification_params.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_client.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_context.h"
-#include "chrome/browser/notifications/scheduler/scheduled_notification_manager.h"
-#include "chrome/browser/notifications/scheduler/user_action_handler.h"
-
-namespace notifications {
-namespace {
-
-class NotificationSchedulerImpl;
-
-// Helper class to do async initialization in parallel for multiple subsystem
-// instances.
-class InitHelper {
- public:
-  using InitCallback = base::OnceCallback<void(bool)>;
-  InitHelper() : weak_ptr_factory_(this) {}
-
-  ~InitHelper() = default;
-
-  // Initializes subsystems in notification scheduler, |callback| will be
-  // invoked if all initializations finished or anyone of them failed. The
-  // object should be destroyed along with the |callback|.
-  void Init(
-      NotificationSchedulerContext* context,
-      ScheduledNotificationManager::Delegate* notification_manager_delegate,
-      ImpressionHistoryTracker::Delegate* impression_tracker_delegate,
-      InitCallback callback) {
-    callback_ = std::move(callback);
-    context->icon_store()->Init(base::BindOnce(
-        &InitHelper::OnIconStoreInitialized, weak_ptr_factory_.GetWeakPtr()));
-    context->impression_tracker()->Init(
-        impression_tracker_delegate,
-        base::BindOnce(&InitHelper::OnImpressionTrackerInitialized,
-                       weak_ptr_factory_.GetWeakPtr()));
-    context->notification_manager()->Init(
-        notification_manager_delegate,
-        base::BindOnce(&InitHelper::OnNotificationManagerInitialized,
-                       weak_ptr_factory_.GetWeakPtr()));
-  }
-
- private:
-  void OnIconStoreInitialized(bool success) {
-    icon_store_initialzed_ = success;
-    MaybeFinishInitialization();
-  }
-
-  void OnImpressionTrackerInitialized(bool success) {
-    impression_tracker_initialzed_ = success;
-    MaybeFinishInitialization();
-  }
-
-  void OnNotificationManagerInitialized(bool success) {
-    notification_manager_initialized_ = success;
-    MaybeFinishInitialization();
-  }
-
-  void MaybeFinishInitialization() {
-    bool all_finished = icon_store_initialzed_.has_value() &&
-                        impression_tracker_initialzed_.has_value() &&
-                        notification_manager_initialized_.has_value();
-    // Notify the initialization result when all subcomponents are initialized.
-    if (!all_finished)
-      return;
-
-    bool success = icon_store_initialzed_.value() &&
-                   impression_tracker_initialzed_.value() &&
-                   notification_manager_initialized_.value();
-    std::move(callback_).Run(success);
-  }
-
-  InitCallback callback_;
-  base::Optional<bool> icon_store_initialzed_;
-  base::Optional<bool> impression_tracker_initialzed_;
-  base::Optional<bool> notification_manager_initialized_;
-
-  base::WeakPtrFactory<InitHelper> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(InitHelper);
-};
-
-// Implementation of NotificationScheduler.
-class NotificationSchedulerImpl
-    : public NotificationScheduler,
-      public NotificationBackgroundTaskScheduler::Handler,
-      public ScheduledNotificationManager::Delegate,
-      public ImpressionHistoryTracker::Delegate,
-      public UserActionHandler {
- public:
-  NotificationSchedulerImpl(
-      std::unique_ptr<NotificationSchedulerContext> context)
-      : context_(std::move(context)), weak_ptr_factory_(this) {}
-
-  ~NotificationSchedulerImpl() override = default;
-
- private:
-  // NotificationScheduler implementation.
-  void Init(InitCallback init_callback) override {
-    auto helper = std::make_unique<InitHelper>();
-    auto* helper_ptr = helper.get();
-    helper_ptr->Init(
-        context_.get(), this, this,
-        base::BindOnce(&NotificationSchedulerImpl::OnInitialized,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(helper),
-                       std::move(init_callback)));
-  }
-
-  void Schedule(
-      std::unique_ptr<NotificationParams> notification_params) override {
-    context_->notification_manager()->ScheduleNotification(
-        std::move(notification_params));
-  }
-
-  void OnInitialized(std::unique_ptr<InitHelper>,
-                     InitCallback init_callback,
-                     bool success) {
-    // TODO(xingliu): Inform the clients about initialization results and tear
-    // down internal components.
-    std::move(init_callback).Run(success);
-  }
-
-  // NotificationBackgroundTaskScheduler::Handler implementation.
-  void OnStartTask() override {
-    // Updates the impression data to compute daily notification shown budget.
-    context_->impression_tracker()->AnalyzeImpressionHistory();
-
-    // TODO(xingliu): Pass SchedulerTaskTime from background task.
-    FindNotificationToShow(SchedulerTaskTime::kMorning);
-
-    // Schedule the next background task based on scheduled notifications.
-    ScheduleBackgroundTask();
-  }
-
-  void OnStopTask() override { ScheduleBackgroundTask(); }
-
-  // ScheduledNotificationManager::Delegate implementation.
-  void DisplayNotification(
-      std::unique_ptr<NotificationEntry> notification) override {
-    // TODO(xingliu): Inform the clients and show the notification.
-    NOTIMPLEMENTED();
-  }
-
-  // ImpressionHistoryTracker::Delegate implementation.
-  void OnImpressionUpdated() override { ScheduleBackgroundTask(); }
-
-  void FindNotificationToShow(SchedulerTaskTime task_start_time) {
-    DisplayDecider::Results results;
-    ScheduledNotificationManager::Notifications notifications;
-    context_->notification_manager()->GetAllNotifications(&notifications);
-
-    DisplayDecider::ClientStates client_states;
-    context_->impression_tracker()->GetClientStates(&client_states);
-
-    std::vector<SchedulerClientType> clients;
-    context_->client_registrar()->GetRegisteredClients(&clients);
-
-    context_->display_decider()->FindNotificationsToShow(
-        context_->config(), std::move(clients), DistributionPolicy::Create(),
-        task_start_time, std::move(notifications), std::move(client_states),
-        &results);
-
-    // TODO(xingliu): Update impression data after notification shown.
-    // See https://crbug.com/965133.
-    for (const auto& guid : results) {
-      context_->notification_manager()->DisplayNotification(guid);
-    }
-  }
-
-  void ScheduleBackgroundTask() {
-    // TODO(xingliu): Implements a class to determine the next background task
-    // based on scheduled notification data.
-    NOTIMPLEMENTED();
-  }
-
-  void OnClick(const std::string& notification_id) override {
-    context_->impression_tracker()->OnClick(notification_id);
-  }
-
-  void OnActionClick(const std::string& notification_id,
-                     ActionButtonType button_type) override {
-    context_->impression_tracker()->OnActionClick(notification_id, button_type);
-  }
-
-  void OnDismiss(const std::string& notification_id) override {
-    context_->impression_tracker()->OnDismiss(notification_id);
-  }
-
-  std::unique_ptr<NotificationSchedulerContext> context_;
-
-  base::WeakPtrFactory<NotificationSchedulerImpl> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerImpl);
-};
-
-}  // namespace
-
-// static
-std::unique_ptr<NotificationScheduler> NotificationScheduler::Create(
-    std::unique_ptr<NotificationSchedulerContext> context) {
-  return std::make_unique<NotificationSchedulerImpl>(std::move(context));
-}
-
-NotificationScheduler::NotificationScheduler() = default;
-
-NotificationScheduler::~NotificationScheduler() = default;
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler.h b/chrome/browser/notifications/scheduler/notification_scheduler.h
deleted file mode 100644
index 7b3eea2..0000000
--- a/chrome/browser/notifications/scheduler/notification_scheduler.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-
-namespace notifications {
-
-class NotificationSchedulerContext;
-struct NotificationParams;
-
-// Provides notification scheduling and throttling functionalities. This class
-// glues all the subsystems together for notification scheduling system.
-class NotificationScheduler {
- public:
-  using InitCallback = base::OnceCallback<void(bool)>;
-  static std::unique_ptr<NotificationScheduler> Create(
-      std::unique_ptr<NotificationSchedulerContext> context);
-
-  NotificationScheduler();
-  virtual ~NotificationScheduler();
-
-  // Initializes the scheduler.
-  virtual void Init(InitCallback init_callback) = 0;
-
-  // Schedules a notification to show in the future. Throttling logic may apply
-  // based on |notification_params|.
-  virtual void Schedule(
-      std::unique_ptr<NotificationParams> notification_params) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NotificationScheduler);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_client.h b/chrome/browser/notifications/scheduler/notification_scheduler_client.h
deleted file mode 100644
index 1272f6e..0000000
--- a/chrome/browser/notifications/scheduler/notification_scheduler_client.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "chrome/browser/notifications/scheduler/notification_data.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace notifications {
-
-// The client interface to receive events from notification scheduler.
-class NotificationSchedulerClient {
- public:
-  struct DisplayData {
-    NotificationData notification_data;
-    SkBitmap icon;
-  };
-
-  // Defines user actions type.
-  enum class UserActionType {
-    // The user clicks on the notification body.
-    kClick = 0,
-    // The user clicks on the notification button.
-    kButtonClick = 1,
-    // The user dismisses the notification.
-    kDismiss = 2,
-  };
-
-  // Information about button clicks.
-  struct ButtonClickInfo {
-    // Unique id of the button.
-    std::string button_id;
-
-    // Associate impression type for the button.
-    ActionButtonType type = ActionButtonType::kUnknownAction;
-  };
-
-  using DisplayCallback =
-      base::OnceCallback<void(std::unique_ptr<DisplayData>)>;
-
-  NotificationSchedulerClient();
-  virtual ~NotificationSchedulerClient();
-
-  // Called when the notification should be displayed to the user. The clients
-  // can overwrite data in |display_data| and return the updated data in
-  // |callback|.
-  virtual void ShowNotification(std::unique_ptr<DisplayData> display_data,
-                                DisplayCallback callback) = 0;
-
-  // Called when scheduler is initialized, number of notification scheduled for
-  // this type is reported if initialization succeeded.
-  virtual void OnSchedulerInitialized(bool success,
-                                      std::set<std::string> guids) = 0;
-
-  // Called when the user interacts with the notification.
-  virtual void OnUserAction(UserActionType action_type,
-                            const std::string& notification_id,
-                            base::Optional<ButtonClickInfo> button_info) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerClient);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.cc b/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.cc
deleted file mode 100644
index f753f7d..0000000
--- a/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_scheduler_client_registrar.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_client.h"
-
-namespace notifications {
-
-NotificationSchedulerClientRegistrar::NotificationSchedulerClientRegistrar() =
-    default;
-
-NotificationSchedulerClientRegistrar::~NotificationSchedulerClientRegistrar() =
-    default;
-
-void NotificationSchedulerClientRegistrar::RegisterClient(
-    SchedulerClientType type,
-    std::unique_ptr<NotificationSchedulerClient> client) {
-  DCHECK(clients_.find(type) == clients_.end());
-  clients_.emplace(type, std::move(client));
-}
-
-NotificationSchedulerClient* NotificationSchedulerClientRegistrar::GetClient(
-    SchedulerClientType type) {
-  auto it = clients_.find(type);
-  if (it == clients_.end())
-    return nullptr;
-  return it->second.get();
-}
-
-void NotificationSchedulerClientRegistrar::GetRegisteredClients(
-    std::vector<SchedulerClientType>* clients) const {
-  DCHECK(clients);
-  clients->clear();
-  for (const auto& pair : clients_) {
-    clients->emplace_back(pair.first);
-  }
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h b/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h
deleted file mode 100644
index ed463cb..0000000
--- a/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace notifications {
-
-class NotificationSchedulerClient;
-
-// Registers and maintains a list of NotificationSchedulerClient
-// implementations.
-class NotificationSchedulerClientRegistrar {
- public:
-  NotificationSchedulerClientRegistrar();
-  ~NotificationSchedulerClientRegistrar();
-
-  // Registers a client into notification scheduler system.
-  void RegisterClient(SchedulerClientType type,
-                      std::unique_ptr<NotificationSchedulerClient> client);
-
-  // Gets a NotificationSchedulerClient, nullptr if the type doesn't exist.
-  NotificationSchedulerClient* GetClient(SchedulerClientType type);
-
-  // Gets a list of registered clients, sorted by integer value of
-  // SchedulerClientType.
-  void GetRegisteredClients(std::vector<SchedulerClientType>* clients) const;
-
- private:
-  using ClientsMap = std::map<SchedulerClientType,
-                              std::unique_ptr<NotificationSchedulerClient>>;
-  ClientsMap clients_;
-
-  DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerClientRegistrar);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_context.cc b/chrome/browser/notifications/scheduler/notification_scheduler_context.cc
deleted file mode 100644
index 799dd68..0000000
--- a/chrome/browser/notifications/scheduler/notification_scheduler_context.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_scheduler_context.h"
-
-#include <utility>
-
-#include "chrome/browser/notifications/scheduler/display_decider.h"
-#include "chrome/browser/notifications/scheduler/icon_store.h"
-#include "chrome/browser/notifications/scheduler/impression_history_tracker.h"
-#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h"
-#include "chrome/browser/notifications/scheduler/scheduled_notification_manager.h"
-#include "chrome/browser/notifications/scheduler/scheduler_config.h"
-
-namespace notifications {
-
-NotificationSchedulerContext::NotificationSchedulerContext(
-    std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar,
-    std::unique_ptr<NotificationBackgroundTaskScheduler> scheduler,
-    std::unique_ptr<IconStore> icon_store,
-    std::unique_ptr<ImpressionHistoryTracker> impression_tracker,
-    std::unique_ptr<ScheduledNotificationManager> notification_manager,
-    std::unique_ptr<DisplayDecider> display_decider,
-    std::unique_ptr<SchedulerConfig> config)
-    : client_registrar_(std::move(client_registrar)),
-      background_task_scheduler_(std::move(scheduler)),
-      impression_tracker_(std::move(impression_tracker)),
-      notification_manager_(std::move(notification_manager)),
-      display_decider_(std::move(display_decider)),
-      config_(std::move(config)) {}
-
-NotificationSchedulerContext::~NotificationSchedulerContext() = default;
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_context.h b/chrome/browser/notifications/scheduler/notification_scheduler_context.h
deleted file mode 100644
index 87d2a52..0000000
--- a/chrome/browser/notifications/scheduler/notification_scheduler_context.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CONTEXT_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CONTEXT_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace notifications {
-
-class DisplayDecider;
-class IconStore;
-class ImpressionHistoryTracker;
-class NotificationBackgroundTaskScheduler;
-class NotificationSchedulerClientRegistrar;
-class ScheduledNotificationManager;
-struct SchedulerConfig;
-
-// Context that contains necessary components needed by the notification
-// scheduler to perform tasks.
-class NotificationSchedulerContext {
- public:
-  NotificationSchedulerContext(
-      std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar,
-      std::unique_ptr<NotificationBackgroundTaskScheduler> scheduler,
-      std::unique_ptr<IconStore> icon_store,
-      std::unique_ptr<ImpressionHistoryTracker> impression_tracker,
-      std::unique_ptr<ScheduledNotificationManager> notification_manager,
-      std::unique_ptr<DisplayDecider> display_decider,
-      std::unique_ptr<SchedulerConfig> config);
-  ~NotificationSchedulerContext();
-
-  NotificationSchedulerClientRegistrar* client_registrar() {
-    return client_registrar_.get();
-  }
-
-  NotificationBackgroundTaskScheduler* background_task_scheduler() {
-    return background_task_scheduler_.get();
-  }
-
-  IconStore* icon_store() { return icon_store_.get(); }
-
-  ImpressionHistoryTracker* impression_tracker() {
-    return impression_tracker_.get();
-  }
-
-  ScheduledNotificationManager* notification_manager() {
-    return notification_manager_.get();
-  }
-
-  DisplayDecider* display_decider() { return display_decider_.get(); }
-
-  const SchedulerConfig* config() const { return config_.get(); }
-
- private:
-  // Holds a list of clients using the notification scheduler system.
-  std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar_;
-
-  // Used to schedule background task in OS level.
-  std::unique_ptr<NotificationBackgroundTaskScheduler>
-      background_task_scheduler_;
-
-  // Stores notification icons.
-  std::unique_ptr<IconStore> icon_store_;
-
-  // Tracks user impressions towards specific notification type.
-  std::unique_ptr<ImpressionHistoryTracker> impression_tracker_;
-
-  // Stores all scheduled notifications.
-  std::unique_ptr<ScheduledNotificationManager> notification_manager_;
-
-  // Helper class to decide which notification should be displayed to the user.
-  std::unique_ptr<DisplayDecider> display_decider_;
-
-  // System configuration.
-  std::unique_ptr<SchedulerConfig> config_;
-
-  DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerContext);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CONTEXT_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/notification_scheduler_types.h
deleted file mode 100644
index 2bb7488..0000000
--- a/chrome/browser/notifications/scheduler/notification_scheduler_types.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_
-
-namespace notifications {
-
-// Enum to describe the time to process scheduled notification data.
-// A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: (
-//   org.chromium.chrome.browser.notifications.scheduler)
-enum class SchedulerTaskTime {
-  // The system is started from normal user launch or other background
-  // tasks.
-  kUnknown = 0,
-  // Background task runs in the morning.
-  kMorning = 1,
-  // Background task runs in the evening.
-  kEvening = 2,
-};
-
-// The type of a list of clients using the notification scheduler system.
-enum class SchedulerClientType {
-  // Test only values.
-  kTest1 = -1,
-  kTest2 = -2,
-  kTest3 = -3,
-
-  // Default value of client type.
-  kUnknown = 0,
-  kMaxValue = kUnknown
-};
-
-// The type of user feedback from a displayed notification.
-enum class UserFeedback {
-  // No user feedback yet.
-  kNoFeedback = 0,
-  // The user taps the helpful button, potentially a strong indicator of user's
-  // positive preference on the notification.
-  kHelpful = 1,
-  // The user taps the unhelpful button, potentially a strong indicator of
-  // user's negative preference on the notification.
-  kNotHelpful = 2,
-  // The user clicks the notification.
-  kClick = 3,
-  // The user clicks the body of the notification.
-  kDismiss = 4,
-  // The user has no interaction with the notification for a while.
-  kIgnore = 5,
-  kMaxValue = kIgnore
-};
-
-// The user impression of a particular notification, which may impact the
-// notification display frenquency.
-enum class ImpressionResult {
-  // Invalid user impression.
-  kInvalid = 0,
-  // Positive user impression that the user may like the notification.
-  kPositive = 1,
-  // Positive user impression that the user may dislike the notification.
-  kNegative = 2,
-  // The feedback is neutral to the user.
-  kNeutral = 3,
-  kMaxValue = kNeutral
-};
-
-// Categorizes type of notification buttons. Different type of button clicks
-// may result in change of notification shown frequency.
-enum class ActionButtonType {
-  // The action button is not categorized.
-  kUnknownAction = 0,
-
-  // Helpful button indicates the user likes to interact with the notification.
-  kHelpful = 1,
-
-  // Unhelpful button indicates dislike of the notification.
-  kUnhelpful = 2,
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/notification_store.cc b/chrome/browser/notifications/scheduler/notification_store.cc
deleted file mode 100644
index 0bc756e..0000000
--- a/chrome/browser/notifications/scheduler/notification_store.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_store.h"
-
-#include "base/bind.h"
-#include "chrome/browser/notifications/scheduler/proto_conversion.h"
-
-namespace leveldb_proto {
-void DataToProto(notifications::NotificationEntry* entry,
-                 notifications::proto::NotificationEntry* proto) {
-  NotificationEntryToProto(entry, proto);
-}
-
-void ProtoToData(notifications::proto::NotificationEntry* proto,
-                 notifications::NotificationEntry* entry) {
-  NotificationEntryFromProto(proto, entry);
-}
-}  // namespace leveldb_proto
-
-namespace notifications {
-
-NotificationStore::NotificationStore(
-    std::unique_ptr<leveldb_proto::ProtoDatabase<proto::NotificationEntry,
-                                                 NotificationEntry>> db)
-    : db_(std::move(db)), weak_ptr_factory_(this) {}
-
-NotificationStore::~NotificationStore() = default;
-
-void NotificationStore::InitAndLoad(LoadCallback callback) {
-  db_->Init(base::BindOnce(&NotificationStore::OnDbInitialized,
-                           weak_ptr_factory_.GetWeakPtr(),
-                           std::move(callback)));
-}
-
-void NotificationStore::OnDbInitialized(
-    LoadCallback callback,
-    leveldb_proto::Enums::InitStatus status) {
-  if (status != leveldb_proto::Enums::InitStatus::kOK) {
-    std::move(callback).Run(false, Entries());
-    return;
-  }
-
-  // Load the data after a successful initialization.
-  db_->LoadEntries(base::BindOnce(&NotificationStore::OnDataLoaded,
-                                  weak_ptr_factory_.GetWeakPtr(),
-                                  std::move(callback)));
-}
-
-void NotificationStore::OnDataLoaded(
-    LoadCallback callback,
-    bool success,
-    std::unique_ptr<EntryVector> entry_vector) {
-  // The database failed to load.
-  if (!success) {
-    std::move(callback).Run(false, Entries());
-    return;
-  }
-
-  // Success to load but no data.
-  if (!entry_vector) {
-    std::move(callback).Run(true, Entries());
-    return;
-  }
-
-  // Load data.
-  Entries entries;
-  for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) {
-    std::unique_ptr<NotificationEntry> notification_entry =
-        std::make_unique<NotificationEntry>(std::move(*it));
-    entries.emplace_back(std::move(notification_entry));
-  }
-
-  std::move(callback).Run(true, std::move(entries));
-}
-
-void NotificationStore::Add(const std::string& key,
-                            const NotificationEntry& entry,
-                            UpdateCallback callback) {
-  Update(key, entry, std::move(callback));
-}
-
-void NotificationStore::Update(const std::string& key,
-                               const NotificationEntry& entry,
-                               UpdateCallback callback) {
-  auto entries_to_save = std::make_unique<KeyEntryVector>();
-  entries_to_save->emplace_back(std::make_pair(key, entry));
-  db_->UpdateEntries(std::move(entries_to_save),
-                     std::make_unique<KeyVector>() /*keys_to_remove*/,
-                     std::move(callback));
-}
-
-void NotificationStore::Delete(const std::string& key,
-                               UpdateCallback callback) {
-  auto keys_to_delete = std::make_unique<KeyVector>();
-  keys_to_delete->emplace_back(key);
-  db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/,
-                     std::move(keys_to_delete), std::move(callback));
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_store.h b/chrome/browser/notifications/scheduler/notification_store.h
deleted file mode 100644
index f76e0c4..0000000
--- a/chrome/browser/notifications/scheduler/notification_store.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_STORE_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_STORE_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/notifications/proto/notification_entry.pb.h"
-#include "chrome/browser/notifications/scheduler/collection_store.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
-#include "components/leveldb_proto/public/proto_database.h"
-
-// Forward declaration for proto conversion.
-namespace leveldb_proto {
-void DataToProto(notifications::NotificationEntry* entry,
-                 notifications::proto::NotificationEntry* proto);
-
-void ProtoToData(notifications::proto::NotificationEntry* proto,
-                 notifications::NotificationEntry* entry);
-}  // namespace leveldb_proto
-
-namespace notifications {
-
-class NotificationStore : public CollectionStore<NotificationEntry> {
- public:
-  NotificationStore(
-      std::unique_ptr<leveldb_proto::ProtoDatabase<proto::NotificationEntry,
-                                                   NotificationEntry>> db);
-  ~NotificationStore() override;
-
- private:
-  using KeyEntryVector = std::vector<std::pair<std::string, NotificationEntry>>;
-  using KeyVector = std::vector<std::string>;
-  using EntryVector = std::vector<NotificationEntry>;
-
-  // CollectionStore<NotificationEntry> implementation.
-  void InitAndLoad(LoadCallback callback) override;
-  void Add(const std::string& key,
-           const NotificationEntry& entry,
-           UpdateCallback callback) override;
-  void Update(const std::string& key,
-              const NotificationEntry& entry,
-              UpdateCallback callback) override;
-  void Delete(const std::string& key, UpdateCallback callback) override;
-
-  // Called when the proto database is initialized but no yet loading the data
-  // into memory.
-  void OnDbInitialized(LoadCallback callback,
-                       leveldb_proto::Enums::InitStatus status);
-
-  // Called when data is loaded from |db_|.
-  void OnDataLoaded(LoadCallback callback,
-                    bool success,
-                    std::unique_ptr<EntryVector> entry_vector);
-
-  // Level db instance to persist data.
-  std::unique_ptr<
-      leveldb_proto::ProtoDatabase<proto::NotificationEntry, NotificationEntry>>
-      db_;
-
-  base::WeakPtrFactory<NotificationStore> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(NotificationStore);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/notification_store_unittests.cc b/chrome/browser/notifications/scheduler/notification_store_unittests.cc
deleted file mode 100644
index 69e834b5..0000000
--- a/chrome/browser/notifications/scheduler/notification_store_unittests.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/notification_store.h"
-
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/notifications/scheduler/proto_conversion.h"
-#include "chrome/browser/notifications/scheduler/test/test_utils.h"
-#include "components/leveldb_proto/public/proto_database.h"
-#include "components/leveldb_proto/testing/fake_db.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using leveldb_proto::test::FakeDB;
-using InitStatus = leveldb_proto::Enums::InitStatus;
-using Entries = notifications::NotificationStore::Entries;
-using TestNotificationEntries =
-    std::map<std::string, notifications::NotificationEntry>;
-using DbEntries = std::vector<notifications::NotificationEntry>;
-using DbEntriesPtr =
-    std::unique_ptr<std::vector<notifications::NotificationEntry>>;
-
-namespace notifications {
-namespace {
-
-const char kGuid[] = "1234";
-
-class NotificationStoreTest : public testing::Test {
- public:
-  NotificationStoreTest() : load_result_(false) {}
-  ~NotificationStoreTest() override = default;
-
-  void SetUp() override {}
-
- protected:
-  void Init(const TestNotificationEntries& test_data, InitStatus status) {
-    CreateTestProtos(test_data);
-    auto db =
-        std::make_unique<FakeDB<proto::NotificationEntry, NotificationEntry>>(
-            &db_protos_);
-    db_ = db.get();
-    store_ = std::make_unique<NotificationStore>(std::move(db));
-    store_->InitAndLoad(base::BindOnce(&NotificationStoreTest::OnEntriesLoaded,
-                                       base::Unretained(this)));
-    db_->InitStatusCallback(status);
-  }
-
-  bool load_result() const { return load_result_; }
-  const Entries& loaded_entries() const { return loaded_entries_; }
-  FakeDB<proto::NotificationEntry, NotificationEntry>* db() { return db_; }
-  CollectionStore<NotificationEntry>* store() { return store_.get(); }
-
-  void VerifyDataInDb(DbEntriesPtr expected) {
-    db_->LoadEntries(base::BindOnce(
-        [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) {
-          EXPECT_TRUE(success);
-          DCHECK(entries);
-          DCHECK(expected);
-          EXPECT_EQ(entries->size(), expected->size());
-          for (size_t i = 0, size = entries->size(); i < size; ++i) {
-            auto& entry = (*entries)[i];
-            auto& expected_entry = (*expected)[i];
-            EXPECT_EQ(entry, expected_entry)
-                << " \n Output: " << test::DebugString(&entry)
-                << " \n Expected: " << test::DebugString(&expected_entry);
-          }
-        },
-        std::move(expected)));
-    db_->LoadCallback(true);
-  }
-
- private:
-  // Push data into |db_|.
-  void CreateTestProtos(const TestNotificationEntries& test_data) {
-    for (const auto& pair : test_data) {
-      const auto& key = pair.first;
-      auto entry = pair.second;
-      proto::NotificationEntry proto;
-      NotificationEntryToProto(&entry, &proto);
-      db_protos_.emplace(key, proto);
-    }
-  }
-
-  void OnEntriesLoaded(bool success, Entries entries) {
-    load_result_ = success;
-    loaded_entries_ = std::move(entries);
-  }
-
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  // Database test objects.
-  FakeDB<proto::NotificationEntry, NotificationEntry>* db_;
-  std::map<std::string, proto::NotificationEntry> db_protos_;
-
-  std::unique_ptr<CollectionStore<NotificationEntry>> store_;
-  Entries loaded_entries_;
-  bool load_result_;
-
-  DISALLOW_COPY_AND_ASSIGN(NotificationStoreTest);
-};
-
-// Verifies initialization with empty database.
-TEST_F(NotificationStoreTest, Init) {
-  Init(TestNotificationEntries(), InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-  EXPECT_TRUE(loaded_entries().empty());
-}
-
-// Initialize non-empty database should success.
-TEST_F(NotificationStoreTest, InitSuccessWithData) {
-  auto test_data = TestNotificationEntries();
-  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
-  bool success =
-      base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time);
-  DCHECK(success);
-  test_data.emplace(kGuid, entry);
-  Init(test_data, InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-  EXPECT_EQ(loaded_entries().size(), 1u);
-  EXPECT_EQ(*loaded_entries().back(), entry);
-}
-
-// Failed database initialization will result in error.
-TEST_F(NotificationStoreTest, InitFailed) {
-  Init(TestNotificationEntries(), InitStatus::kCorrupt);
-  EXPECT_EQ(load_result(), false);
-  EXPECT_TRUE(loaded_entries().empty());
-}
-
-TEST_F(NotificationStoreTest, AddAndUpdate) {
-  Init(TestNotificationEntries(), InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(load_result(), true);
-  EXPECT_TRUE(loaded_entries().empty());
-
-  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
-  bool success =
-      base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time);
-  DCHECK(success);
-
-  // Add data to the store and verify the database.
-  store()->Add(kGuid, entry,
-               base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
-  db()->UpdateCallback(true);
-  auto expected = std::make_unique<DbEntries>();
-  expected->emplace_back(entry);
-  VerifyDataInDb(std::move(expected));
-
-  // Update and verified the new data.
-  entry.notification_data.title = "test_title";
-  expected = std::make_unique<DbEntries>();
-  expected->emplace_back(entry);
-  store()->Update(kGuid, entry,
-                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
-  db()->UpdateCallback(true);
-  VerifyDataInDb(std::move(expected));
-}
-
-TEST_F(NotificationStoreTest, Delete) {
-  auto test_data = TestNotificationEntries();
-  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
-  test_data.emplace(kGuid, entry);
-  Init(test_data, InitStatus::kOK);
-  db()->LoadCallback(true);
-  EXPECT_EQ(loaded_entries().size(), 1u);
-
-  // Delete the entry and verify data is deleted.
-  store()->Delete(kGuid, base::DoNothing());
-  db()->UpdateCallback(true);
-  VerifyDataInDb(std::make_unique<DbEntries>());
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/proto_conversion.cc b/chrome/browser/notifications/scheduler/proto_conversion.cc
deleted file mode 100644
index 8322989..0000000
--- a/chrome/browser/notifications/scheduler/proto_conversion.cc
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/proto_conversion.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/logging.h"
-
-namespace notifications {
-
-namespace {
-
-// Helper method to convert base::TimeDelta to integer for serialization. Loses
-// precision beyond miliseconds.
-int64_t TimeDeltaToMilliseconds(const base::TimeDelta& delta) {
-  return delta.InMilliseconds();
-}
-
-// Helper method to convert serialized time delta as integer to base::TimeDelta
-// for deserialization. Loses precision beyond miliseconds.
-base::TimeDelta MillisecondsToTimeDelta(int64_t serialized_delat_ms) {
-  return base::TimeDelta::FromMilliseconds(serialized_delat_ms);
-}
-
-// Helper method to convert base::Time to integer for serialization. Loses
-// precision beyond miliseconds.
-int64_t TimeToMilliseconds(const base::Time& time) {
-  return time.ToDeltaSinceWindowsEpoch().InMilliseconds();
-}
-
-// Helper method to convert serialized time as integer to base::Time for
-// deserialization. Loses precision beyond miliseconds.
-base::Time MillisecondsToTime(int64_t serialized_time_ms) {
-  return base::Time::FromDeltaSinceWindowsEpoch(
-      base::TimeDelta::FromMilliseconds(serialized_time_ms));
-}
-
-// Converts SchedulerClientType to its associated enum in proto buffer.
-proto::SchedulerClientType ToSchedulerClientType(SchedulerClientType type) {
-  switch (type) {
-    case SchedulerClientType::kTest1:
-      return proto::SchedulerClientType::TEST_1;
-    case SchedulerClientType::kTest2:
-      return proto::SchedulerClientType::TEST_2;
-    case SchedulerClientType::kTest3:
-      return proto::SchedulerClientType::TEST_3;
-    case SchedulerClientType::kUnknown:
-      return proto::SchedulerClientType::UNKNOWN;
-  }
-  NOTREACHED();
-}
-
-// Converts SchedulerClientType from its associated enum in proto buffer.
-SchedulerClientType FromSchedulerClientType(
-    proto::SchedulerClientType proto_type) {
-  switch (proto_type) {
-    case proto::SchedulerClientType::TEST_1:
-      return SchedulerClientType::kTest1;
-    case proto::SchedulerClientType::TEST_2:
-      return SchedulerClientType::kTest2;
-    case proto::SchedulerClientType::TEST_3:
-      return SchedulerClientType::kTest3;
-    case proto::SchedulerClientType::UNKNOWN:
-      return SchedulerClientType::kUnknown;
-  }
-  NOTREACHED();
-}
-
-// Converts UserFeedback to its associated enum in proto buffer.
-proto::Impression_UserFeedback ToUserFeedback(UserFeedback feedback) {
-  switch (feedback) {
-    case UserFeedback::kNoFeedback:
-      return proto::Impression_UserFeedback_NO_FEEDBACK;
-    case UserFeedback::kHelpful:
-      return proto::Impression_UserFeedback_HELPFUL;
-    case UserFeedback::kNotHelpful:
-      return proto::Impression_UserFeedback_NOT_HELPFUL;
-    case UserFeedback::kClick:
-      return proto::Impression_UserFeedback_CLICK;
-    case UserFeedback::kDismiss:
-      return proto::Impression_UserFeedback_DISMISS;
-    case UserFeedback::kIgnore:
-      return proto::Impression_UserFeedback_IGNORE;
-  }
-  NOTREACHED();
-}
-
-// Converts UserFeedback from its associated enum in proto buffer.
-UserFeedback FromUserFeedback(proto::Impression_UserFeedback feedback) {
-  switch (feedback) {
-    case proto::Impression_UserFeedback_NO_FEEDBACK:
-      return UserFeedback::kNoFeedback;
-    case proto::Impression_UserFeedback_HELPFUL:
-      return UserFeedback::kHelpful;
-    case proto::Impression_UserFeedback_NOT_HELPFUL:
-      return UserFeedback::kNotHelpful;
-    case proto::Impression_UserFeedback_CLICK:
-      return UserFeedback::kClick;
-    case proto::Impression_UserFeedback_DISMISS:
-      return UserFeedback::kDismiss;
-    case proto::Impression_UserFeedback_IGNORE:
-      return UserFeedback::kIgnore;
-  }
-  NOTREACHED();
-}
-
-// Converts ImpressionResult to its associated enum in proto buffer.
-proto::Impression_ImpressionResult ToImpressionResult(ImpressionResult result) {
-  switch (result) {
-    case ImpressionResult::kInvalid:
-      return proto::Impression_ImpressionResult_INVALID;
-    case ImpressionResult::kPositive:
-      return proto::Impression_ImpressionResult_POSITIVE;
-    case ImpressionResult::kNegative:
-      return proto::Impression_ImpressionResult_NEGATIVE;
-    case ImpressionResult::kNeutral:
-      return proto::Impression_ImpressionResult_NEUTRAL;
-  }
-  NOTREACHED();
-}
-
-// Converts ImpressionResult from its associated enum in proto buffer.
-ImpressionResult FromImpressionResult(
-    proto::Impression_ImpressionResult result) {
-  switch (result) {
-    case proto::Impression_ImpressionResult_INVALID:
-      return ImpressionResult::kInvalid;
-    case proto::Impression_ImpressionResult_POSITIVE:
-      return ImpressionResult::kPositive;
-    case proto::Impression_ImpressionResult_NEGATIVE:
-      return ImpressionResult::kNegative;
-    case proto::Impression_ImpressionResult_NEUTRAL:
-      return ImpressionResult::kNeutral;
-  }
-  NOTREACHED();
-}
-
-// Converts ImpressionResult to its associated enum in proto buffer.
-proto::Impression_SchedulerTaskTime ToSchedulerTaskTime(
-    SchedulerTaskTime time) {
-  switch (time) {
-    case SchedulerTaskTime::kUnknown:
-      return proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME;
-    case SchedulerTaskTime::kMorning:
-      return proto::Impression_SchedulerTaskTime_MORNING;
-    case SchedulerTaskTime::kEvening:
-      return proto::Impression_SchedulerTaskTime_EVENING;
-  }
-  NOTREACHED();
-}
-
-// Converts ImpressionResult from its associated enum in proto buffer.
-SchedulerTaskTime FromSchedulerTaskTime(
-    proto::Impression_SchedulerTaskTime time) {
-  switch (time) {
-    case proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME:
-      return SchedulerTaskTime::kUnknown;
-    case proto::Impression_SchedulerTaskTime_MORNING:
-      return SchedulerTaskTime::kMorning;
-    case proto::Impression_SchedulerTaskTime_EVENING:
-      return SchedulerTaskTime::kEvening;
-  }
-  NOTREACHED();
-}
-
-// Converts NotificationData to proto buffer type.
-void NotificationDataToProto(NotificationData* notification_data,
-                             proto::NotificationData* proto) {
-  proto->set_id(notification_data->id);
-  proto->set_title(notification_data->title);
-  proto->set_message(notification_data->message);
-  proto->set_icon_uuid(notification_data->icon_uuid);
-  proto->set_url(notification_data->url);
-}
-
-// Converts NotificationData from proto buffer type.
-void NotificationDataFromProto(proto::NotificationData* proto,
-                               NotificationData* notification_data) {
-  notification_data->id = proto->id();
-  notification_data->title = proto->title();
-  notification_data->message = proto->message();
-  notification_data->icon_uuid = proto->icon_uuid();
-  notification_data->url = proto->url();
-}
-
-// Converts ScheduleParams::Priority to proto buffer type.
-proto::ScheduleParams_Priority ScheduleParamsPriorityToProto(
-    ScheduleParams::Priority priority) {
-  using Priority = ScheduleParams::Priority;
-  switch (priority) {
-    case Priority::kLow:
-      return proto::ScheduleParams_Priority_LOW;
-    case Priority::kHigh:
-      return proto::ScheduleParams_Priority_HIGH;
-    case Priority::kNoThrottle:
-      return proto::ScheduleParams_Priority_NO_THROTTLE;
-  }
-}
-
-// Converts ScheduleParams::Priority from proto buffer type.
-ScheduleParams::Priority ScheduleParamsPriorityFromProto(
-    proto::ScheduleParams_Priority priority) {
-  using Priority = ScheduleParams::Priority;
-  switch (priority) {
-    case proto::ScheduleParams_Priority_LOW:
-      return Priority::kLow;
-    case proto::ScheduleParams_Priority_HIGH:
-      return Priority::kHigh;
-    case proto::ScheduleParams_Priority_NO_THROTTLE:
-      return Priority::kNoThrottle;
-  }
-}
-
-// Converts ScheduleParams to proto buffer type.
-void ScheduleParamsToProto(ScheduleParams* params,
-                           proto::ScheduleParams* proto) {
-  proto->set_priority(ScheduleParamsPriorityToProto(params->priority));
-}
-
-// Converts ScheduleParams from proto buffer type.
-void ScheduleParamsFromProto(proto::ScheduleParams* proto,
-                             ScheduleParams* params) {
-  params->priority = ScheduleParamsPriorityFromProto(proto->priority());
-}
-
-}  // namespace
-
-void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto) {
-  proto->mutable_uuid()->swap(entry->uuid);
-  proto->mutable_icon()->swap(entry->data);
-}
-
-void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry) {
-  DCHECK(proto->has_uuid());
-  DCHECK(proto->has_icon());
-  entry->data.swap(*proto->mutable_icon());
-  entry->uuid.swap(*proto->mutable_uuid());
-}
-
-void ClientStateToProto(ClientState* client_state,
-                        notifications::proto::ClientState* proto) {
-  proto->set_type(ToSchedulerClientType(client_state->type));
-  proto->set_current_max_daily_show(client_state->current_max_daily_show);
-
-  for (const auto& impression : client_state->impressions) {
-    auto* impression_ptr = proto->add_impressions();
-    impression_ptr->set_create_time(TimeToMilliseconds(impression.create_time));
-    impression_ptr->set_feedback(ToUserFeedback(impression.feedback));
-    impression_ptr->set_impression(ToImpressionResult(impression.impression));
-    impression_ptr->set_integrated(impression.integrated);
-    impression_ptr->set_task_start_time(
-        ToSchedulerTaskTime(impression.task_start_time));
-    impression_ptr->set_guid(impression.guid);
-  }
-
-  if (client_state->suppression_info.has_value()) {
-    const auto& suppression = *client_state->suppression_info;
-    auto* suppression_proto = proto->mutable_suppression_info();
-    suppression_proto->set_last_trigger_time(
-        TimeToMilliseconds(suppression.last_trigger_time));
-    suppression_proto->set_duration_ms(
-        TimeDeltaToMilliseconds(suppression.duration));
-    suppression_proto->set_recover_goal(suppression.recover_goal);
-  }
-}
-
-void ClientStateFromProto(proto::ClientState* proto,
-                          notifications::ClientState* client_state) {
-  DCHECK(proto->has_type());
-  DCHECK(proto->has_current_max_daily_show());
-  client_state->type = FromSchedulerClientType(proto->type());
-  client_state->current_max_daily_show = proto->current_max_daily_show();
-
-  for (const auto& proto_impression : proto->impressions()) {
-    Impression impression;
-    DCHECK(proto_impression.has_create_time());
-    impression.create_time = MillisecondsToTime(proto_impression.create_time());
-    impression.feedback = FromUserFeedback(proto_impression.feedback());
-    impression.impression = FromImpressionResult(proto_impression.impression());
-    impression.integrated = proto_impression.integrated();
-    impression.task_start_time =
-        FromSchedulerTaskTime(proto_impression.task_start_time());
-    impression.guid = proto_impression.guid();
-    impression.type = client_state->type;
-    client_state->impressions.emplace_back(std::move(impression));
-  }
-
-  if (proto->has_suppression_info()) {
-    const auto& proto_suppression = proto->suppression_info();
-    DCHECK(proto_suppression.has_last_trigger_time());
-    DCHECK(proto_suppression.has_duration_ms());
-    DCHECK(proto_suppression.has_recover_goal());
-
-    SuppressionInfo suppression_info(
-        MillisecondsToTime(proto_suppression.last_trigger_time()),
-        MillisecondsToTimeDelta(proto_suppression.duration_ms()));
-    suppression_info.recover_goal = proto_suppression.recover_goal();
-    client_state->suppression_info = std::move(suppression_info);
-  }
-}
-
-void NotificationEntryToProto(NotificationEntry* entry,
-                              proto::NotificationEntry* proto) {
-  proto->set_type(ToSchedulerClientType(entry->type));
-  proto->set_guid(entry->guid);
-  proto->set_create_time(TimeToMilliseconds(entry->create_time));
-  auto* proto_notification_data = proto->mutable_notification_data();
-  NotificationDataToProto(&entry->notification_data, proto_notification_data);
-  auto* proto_schedule_params = proto->mutable_schedule_params();
-  ScheduleParamsToProto(&entry->schedule_params, proto_schedule_params);
-}
-
-void NotificationEntryFromProto(proto::NotificationEntry* proto,
-                                NotificationEntry* entry) {
-  entry->type = FromSchedulerClientType(proto->type());
-  entry->guid = proto->guid();
-  entry->create_time = MillisecondsToTime(proto->create_time());
-  NotificationDataFromProto(proto->mutable_notification_data(),
-                            &entry->notification_data);
-  ScheduleParamsFromProto(proto->mutable_schedule_params(),
-                          &entry->schedule_params);
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/proto_conversion.h b/chrome/browser/notifications/scheduler/proto_conversion.h
deleted file mode 100644
index 333f421..0000000
--- a/chrome/browser/notifications/scheduler/proto_conversion.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PROTO_CONVERSION_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PROTO_CONVERSION_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/notifications/proto/client_state.pb.h"
-#include "chrome/browser/notifications/proto/icon.pb.h"
-#include "chrome/browser/notifications/proto/notification_entry.pb.h"
-#include "chrome/browser/notifications/scheduler/icon_entry.h"
-#include "chrome/browser/notifications/scheduler/impression_types.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
-
-namespace notifications {
-
-// Converts an icon entry to icon proto.
-void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto);
-
-// Converts an icon proto to icon entry.
-void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry);
-
-// Converts client state to proto.
-void ClientStateToProto(ClientState* client_state,
-                        notifications::proto::ClientState* proto);
-
-// Converts proto to client state.
-void ClientStateFromProto(proto::ClientState* proto,
-                          notifications::ClientState* client_state);
-
-// Converts notification entry to proto.
-void NotificationEntryToProto(NotificationEntry* entry,
-                              proto::NotificationEntry* proto);
-
-// Converts proto to notification entry.
-void NotificationEntryFromProto(proto::NotificationEntry* proto,
-                                NotificationEntry* entry);
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PROTO_CONVERSION_H_
diff --git a/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc
deleted file mode 100644
index d3b641c..0000000
--- a/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/proto_conversion.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/logging.h"
-#include "chrome/browser/notifications/scheduler/test/test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using IconProto = notifications::proto::Icon;
-
-namespace notifications {
-namespace {
-
-const char kUuid[] = "123";
-const char kGuid[] = "testGuid";
-const char kData[] = "bitmapdata";
-
-void TestClientStateConversion(ClientState* client_state) {
-  DCHECK(client_state);
-  notifications::proto::ClientState proto;
-  ClientState expected;
-  ClientStateToProto(client_state, &proto);
-  ClientStateFromProto(&proto, &expected);
-  EXPECT_EQ(*client_state, expected)
-      << " \n Output: \n " << client_state->DebugPrint() << " \n Expected: \n"
-      << expected.DebugPrint();
-}
-
-void TestNotificationEntryConversion(NotificationEntry* entry) {
-  DCHECK(entry);
-  notifications::proto::NotificationEntry proto;
-  NotificationEntry expected(SchedulerClientType::kTest1, "");
-  NotificationEntryToProto(entry, &proto);
-  NotificationEntryFromProto(&proto, &expected);
-  EXPECT_EQ(entry->notification_data, expected.notification_data);
-  EXPECT_EQ(entry->schedule_params, expected.schedule_params);
-
-  EXPECT_EQ(*entry, expected)
-      << "Output: " << test::DebugString(entry)
-      << " \n Expected: " << test::DebugString(&expected);
-}
-
-TEST(ProtoConversionTest, IconEntryFromProto) {
-  IconProto proto;
-  proto.set_uuid(kUuid);
-  proto.set_icon(kData);
-  IconEntry entry;
-
-  IconEntryFromProto(&proto, &entry);
-
-  // Verify entry data.
-  EXPECT_EQ(entry.uuid, kUuid);
-  EXPECT_EQ(entry.data, kData);
-}
-
-TEST(ProtoConversionTest, IconEntryToProto) {
-  IconEntry entry;
-  entry.data = kData;
-  entry.uuid = kUuid;
-  IconProto proto;
-
-  IconEntryToProto(&entry, &proto);
-
-  // Verify proto data.
-  EXPECT_EQ(proto.icon(), kData);
-  EXPECT_EQ(proto.uuid(), kUuid);
-}
-
-// Verifies client state proto conversion.
-TEST(ProtoConversionTest, ClientStateProtoConversion) {
-  // Verify basic fields.
-  ClientState client_state;
-  test::ImpressionTestData test_data{
-      SchedulerClientType::kTest1, 3, {}, base::nullopt};
-  test::AddImpressionTestData(test_data, &client_state);
-  TestClientStateConversion(&client_state);
-
-  // Verify suppression info.
-  base::Time last_trigger_time;
-  bool success =
-      base::Time::FromString("04/25/20 01:00:00 AM", &last_trigger_time);
-  DCHECK(success);
-  auto duration = base::TimeDelta::FromDays(7);
-  auto suppression = SuppressionInfo(last_trigger_time, duration);
-  suppression.recover_goal = 5;
-  client_state.suppression_info = std::move(suppression);
-  TestClientStateConversion(&client_state);
-}
-
-// Verifies impression proto conversion.
-TEST(ProtoConversionTest, ImpressionProtoConversion) {
-  ClientState client_state;
-  client_state.type = SchedulerClientType::kTest1;
-  base::Time create_time;
-  bool success = base::Time::FromString("03/25/19 00:00:00 AM", &create_time);
-  DCHECK(success);
-  Impression impression{create_time,
-                        UserFeedback::kHelpful,
-                        ImpressionResult::kPositive,
-                        true /*integrated*/,
-                        SchedulerTaskTime::kMorning,
-                        kGuid,
-                        SchedulerClientType::kTest1};
-  client_state.impressions.emplace_back(impression);
-  TestClientStateConversion(&client_state);
-
-  auto& first_impression = *client_state.impressions.begin();
-
-  // Verify all feedback types.
-  std::vector<UserFeedback> feedback_types{
-      UserFeedback::kNoFeedback, UserFeedback::kHelpful,
-      UserFeedback::kNotHelpful, UserFeedback::kClick,
-      UserFeedback::kDismiss,    UserFeedback::kIgnore};
-  for (const auto feedback_type : feedback_types) {
-    first_impression.feedback = feedback_type;
-    TestClientStateConversion(&client_state);
-  }
-
-  // Verify all impression result types.
-  std::vector<ImpressionResult> impression_results{
-      ImpressionResult::kInvalid, ImpressionResult::kPositive,
-      ImpressionResult::kNegative, ImpressionResult::kNeutral};
-  for (const auto impression_result : impression_results) {
-    first_impression.impression = impression_result;
-    TestClientStateConversion(&client_state);
-  }
-
-  // Verify all scheduler task time types.
-  std::vector<SchedulerTaskTime> task_times{SchedulerTaskTime::kUnknown,
-                                            SchedulerTaskTime::kMorning,
-                                            SchedulerTaskTime::kEvening};
-  for (const auto task_start_time : task_times) {
-    first_impression.task_start_time = task_start_time;
-    TestClientStateConversion(&client_state);
-  }
-}
-
-// Verifies multiple impressions are serialized correctly.
-TEST(ProtoConversionTest, MultipleImpressionConversion) {
-  ClientState client_state;
-  base::Time create_time;
-  bool success = base::Time::FromString("04/25/20 01:00:00 AM", &create_time);
-  DCHECK(success);
-
-  Impression impression{create_time, UserFeedback::kHelpful,
-                        ImpressionResult::kPositive, true,
-                        SchedulerTaskTime::kMorning};
-  Impression other_impression{create_time, UserFeedback::kNoFeedback,
-                              ImpressionResult::kNegative, false,
-                              SchedulerTaskTime::kEvening};
-  client_state.impressions.emplace_back(std::move(impression));
-  client_state.impressions.emplace_back(std::move(other_impression));
-  TestClientStateConversion(&client_state);
-}
-
-// Verifies notification entry proto conversion.
-TEST(ProtoConversionTest, NotificationEntryConversion) {
-  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
-  bool success =
-      base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time);
-  DCHECK(success);
-  TestNotificationEntryConversion(&entry);
-
-  // Test notification data.
-  entry.notification_data.id = kGuid;
-  entry.notification_data.title = "title";
-  entry.notification_data.message = "message";
-  entry.notification_data.icon_uuid = "icon_uuid";
-  entry.notification_data.url = "url";
-  TestNotificationEntryConversion(&entry);
-
-  // Test scheduling params.
-  const ScheduleParams::Priority priorities[] = {
-      ScheduleParams::Priority::kLow, ScheduleParams::Priority::kHigh,
-      ScheduleParams::Priority::kNoThrottle};
-  for (auto priority : priorities) {
-    entry.schedule_params.priority = priority;
-    TestNotificationEntryConversion(&entry);
-  }
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/BUILD.gn b/chrome/browser/notifications/scheduler/public/BUILD.gn
new file mode 100644
index 0000000..3ce4de5a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/buildflag_header.gni")
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+}
+
+source_set("public") {
+  sources = [
+    "notification_background_task_scheduler.h",
+    "notification_data.cc",
+    "notification_data.h",
+    "notification_params.cc",
+    "notification_params.h",
+    "notification_schedule_service.h",
+    "notification_scheduler_client.h",
+    "notification_scheduler_client_registrar.cc",
+    "notification_scheduler_client_registrar.h",
+    "notification_scheduler_types.h",
+    "schedule_params.cc",
+    "schedule_params.h",
+    "user_action_handler.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/keyed_service/core",
+    "//skia",
+  ]
+}
+
+if (is_android) {
+  java_cpp_enum("jni_enums") {
+    sources = [
+      "notification_scheduler_types.h",
+    ]
+  }
+}
diff --git a/chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h b/chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h
new file mode 100644
index 0000000..66911063
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h
@@ -0,0 +1,60 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace notifications {
+
+// Interface to schedule a background task on platform OS to run the
+// notification scheduler job.
+class NotificationBackgroundTaskScheduler {
+ public:
+  // Interface used to handle background task events.
+  class Handler {
+   public:
+    // Called when the background task is started.
+    virtual void OnStartTask() = 0;
+
+    // Called when the background task is stopped by the OS when it wants to
+    // reallocate resources, our task is not finished yet in this case. The
+    // handler implementation should explicitly decide whether the task should
+    // be rescheduled and run later.
+    virtual void OnStopTask() = 0;
+
+   protected:
+    Handler() = default;
+    virtual ~Handler() = default;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Handler);
+  };
+
+  // Schedules a background task in a time window between |window_start| and
+  // |window_end|. This will update the current background task. Only one
+  // background task exists for notification scheduler. |scheduler_task_time|
+  // tag is passed to background task go support arbitrary time background task.
+  virtual void Schedule(notifications::SchedulerTaskTime scheduler_task_time,
+                        base::TimeDelta window_start,
+                        base::TimeDelta window_end) = 0;
+
+  // Cancels the background task.
+  virtual void Cancel() = 0;
+
+  virtual ~NotificationBackgroundTaskScheduler() = default;
+
+ protected:
+  NotificationBackgroundTaskScheduler() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NotificationBackgroundTaskScheduler);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.cc b/chrome/browser/notifications/scheduler/public/notification_data.cc
new file mode 100644
index 0000000..ec6b9283
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_data.cc
@@ -0,0 +1,20 @@
+// Copyright 2019 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/notifications/scheduler/public/notification_data.h"
+
+namespace notifications {
+
+NotificationData::NotificationData() = default;
+
+NotificationData::NotificationData(const NotificationData& other) = default;
+
+bool NotificationData::operator==(const NotificationData& other) const {
+  return id == other.id && title == other.title && message == other.message &&
+         icon_uuid == other.icon_uuid && url == other.url;
+}
+
+NotificationData::~NotificationData() = default;
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.h b/chrome/browser/notifications/scheduler/public/notification_data.h
new file mode 100644
index 0000000..2bd89848
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_data.h
@@ -0,0 +1,43 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_DATA_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_DATA_H_
+
+#include <string>
+
+namespace notifications {
+
+// Contains data used to display a scheduled notification. All fields will be
+// persisted to disk as protobuffer NotificationData. The clients of
+// notification scheduler can optionally use the texts or icon in this struct,
+// or use hard coded assets id.
+struct NotificationData {
+  NotificationData();
+  NotificationData(const NotificationData& other);
+  bool operator==(const NotificationData& other) const;
+  ~NotificationData();
+
+  // The unique identifier of the notification. This is not used as the key for
+  // the database entry, but the id of other notification struct plumbed to
+  // the scheduler system.
+  std::string id;
+
+  // The title of the notification.
+  std::string title;
+
+  // The body text of the notification.
+  std::string message;
+
+  // The unique identifier of the icon, which must be loaded asynchronously into
+  // memory.
+  std::string icon_uuid;
+
+  // URL of the web site responsible for showing the notification.
+  std::string url;
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_DATA_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_params.cc b/chrome/browser/notifications/scheduler/public/notification_params.cc
new file mode 100644
index 0000000..7b53c2a
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_params.cc
@@ -0,0 +1,24 @@
+// Copyright 2019 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/notifications/scheduler/public/notification_params.h"
+
+#include <utility>
+
+#include "base/guid.h"
+#include "chrome/browser/notifications/scheduler/public/schedule_params.h"
+
+namespace notifications {
+
+NotificationParams::NotificationParams(SchedulerClientType type,
+                                       NotificationData notification_data,
+                                       ScheduleParams schedule_params)
+    : type(type),
+      guid(base::GenerateGUID()),
+      notification_data(std::move(notification_data)),
+      schedule_params(std::move(schedule_params)) {}
+
+NotificationParams::~NotificationParams() = default;
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_params.h b/chrome/browser/notifications/scheduler/public/notification_params.h
new file mode 100644
index 0000000..24889dc9
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_params.h
@@ -0,0 +1,39 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_PARAMS_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_PARAMS_H_
+
+#include <memory>
+
+#include "chrome/browser/notifications/scheduler/public/notification_data.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+#include "chrome/browser/notifications/scheduler/public/schedule_params.h"
+
+namespace notifications {
+
+// Struct used to schedule a notification.
+struct NotificationParams {
+  NotificationParams(SchedulerClientType type,
+                     NotificationData notification,
+                     ScheduleParams schedule_params);
+  ~NotificationParams();
+
+  // The type of notification using the scheduling system.
+  SchedulerClientType type;
+
+  // An auto generated unique id of the scheduled notification.
+  std::string guid;
+
+  // Data used to show the notification, such as text or title on the
+  // notification.
+  NotificationData notification_data;
+
+  // Scheduling details used to determine when to show the notification.
+  ScheduleParams schedule_params;
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_PARAMS_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_schedule_service.h b/chrome/browser/notifications/scheduler/public/notification_schedule_service.h
new file mode 100644
index 0000000..4a6ae68
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_schedule_service.h
@@ -0,0 +1,37 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULE_SERVICE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULE_SERVICE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace notifications {
+
+struct NotificationParams;
+
+// Service to schedule a notification to display in the future. An internal
+// throttling mechanism will be applied to limit the maximum notification shown
+// to the user. Also user's interaction with the notification will affect the
+// frequency of notification delivery.
+class NotificationScheduleService : public KeyedService {
+ public:
+  // Schedules a notification to display.
+  virtual void Schedule(
+      std::unique_ptr<NotificationParams> notification_params) = 0;
+
+ protected:
+  NotificationScheduleService() = default;
+  ~NotificationScheduleService() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NotificationScheduleService);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULE_SERVICE_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h
new file mode 100644
index 0000000..75916ce
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h
@@ -0,0 +1,77 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "chrome/browser/notifications/scheduler/public/notification_data.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace notifications {
+
+// The client interface to receive events from notification scheduler.
+class NotificationSchedulerClient {
+ public:
+  struct DisplayData {
+    NotificationData notification_data;
+    SkBitmap icon;
+  };
+
+  // Defines user actions type.
+  enum class UserActionType {
+    // The user clicks on the notification body.
+    kClick = 0,
+    // The user clicks on the notification button.
+    kButtonClick = 1,
+    // The user dismisses the notification.
+    kDismiss = 2,
+  };
+
+  // Information about button clicks.
+  struct ButtonClickInfo {
+    // Unique id of the button.
+    std::string button_id;
+
+    // Associate impression type for the button.
+    ActionButtonType type = ActionButtonType::kUnknownAction;
+  };
+
+  using DisplayCallback =
+      base::OnceCallback<void(std::unique_ptr<DisplayData>)>;
+
+  NotificationSchedulerClient();
+  virtual ~NotificationSchedulerClient();
+
+  // Called when the notification should be displayed to the user. The clients
+  // can overwrite data in |display_data| and return the updated data in
+  // |callback|.
+  virtual void ShowNotification(std::unique_ptr<DisplayData> display_data,
+                                DisplayCallback callback) = 0;
+
+  // Called when scheduler is initialized, number of notification scheduled for
+  // this type is reported if initialization succeeded.
+  virtual void OnSchedulerInitialized(bool success,
+                                      std::set<std::string> guids) = 0;
+
+  // Called when the user interacts with the notification.
+  virtual void OnUserAction(UserActionType action_type,
+                            const std::string& notification_id,
+                            base::Optional<ButtonClickInfo> button_info) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerClient);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.cc b/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.cc
new file mode 100644
index 0000000..b5da56a03d
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.cc
@@ -0,0 +1,44 @@
+// Copyright 2019 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/notifications/scheduler/public/notification_scheduler_client_registrar.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client.h"
+
+namespace notifications {
+
+NotificationSchedulerClientRegistrar::NotificationSchedulerClientRegistrar() =
+    default;
+
+NotificationSchedulerClientRegistrar::~NotificationSchedulerClientRegistrar() =
+    default;
+
+void NotificationSchedulerClientRegistrar::RegisterClient(
+    SchedulerClientType type,
+    std::unique_ptr<NotificationSchedulerClient> client) {
+  DCHECK(clients_.find(type) == clients_.end());
+  clients_.emplace(type, std::move(client));
+}
+
+NotificationSchedulerClient* NotificationSchedulerClientRegistrar::GetClient(
+    SchedulerClientType type) {
+  auto it = clients_.find(type);
+  if (it == clients_.end())
+    return nullptr;
+  return it->second.get();
+}
+
+void NotificationSchedulerClientRegistrar::GetRegisteredClients(
+    std::vector<SchedulerClientType>* clients) const {
+  DCHECK(clients);
+  clients->clear();
+  for (const auto& pair : clients_) {
+    clients->emplace_back(pair.first);
+  }
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h
new file mode 100644
index 0000000..5cca83ca
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h
@@ -0,0 +1,47 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+
+namespace notifications {
+
+class NotificationSchedulerClient;
+
+// Registers and maintains a list of NotificationSchedulerClient
+// implementations.
+class NotificationSchedulerClientRegistrar {
+ public:
+  NotificationSchedulerClientRegistrar();
+  ~NotificationSchedulerClientRegistrar();
+
+  // Registers a client into notification scheduler system.
+  void RegisterClient(SchedulerClientType type,
+                      std::unique_ptr<NotificationSchedulerClient> client);
+
+  // Gets a NotificationSchedulerClient, nullptr if the type doesn't exist.
+  NotificationSchedulerClient* GetClient(SchedulerClientType type);
+
+  // Gets a list of registered clients, sorted by integer value of
+  // SchedulerClientType.
+  void GetRegisteredClients(std::vector<SchedulerClientType>* clients) const;
+
+ private:
+  using ClientsMap = std::map<SchedulerClientType,
+                              std::unique_ptr<NotificationSchedulerClient>>;
+  ClientsMap clients_;
+
+  DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerClientRegistrar);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h
new file mode 100644
index 0000000..604b4c9
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h
@@ -0,0 +1,84 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_TYPES_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_TYPES_H_
+
+namespace notifications {
+
+// Enum to describe the time to process scheduled notification data.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.chrome.browser.notifications.scheduler)
+enum class SchedulerTaskTime {
+  // The system is started from normal user launch or other background
+  // tasks.
+  kUnknown = 0,
+  // Background task runs in the morning.
+  kMorning = 1,
+  // Background task runs in the evening.
+  kEvening = 2,
+};
+
+// The type of a list of clients using the notification scheduler system.
+enum class SchedulerClientType {
+  // Test only values.
+  kTest1 = -1,
+  kTest2 = -2,
+  kTest3 = -3,
+
+  // Default value of client type.
+  kUnknown = 0,
+  kMaxValue = kUnknown
+};
+
+// The type of user feedback from a displayed notification.
+enum class UserFeedback {
+  // No user feedback yet.
+  kNoFeedback = 0,
+  // The user taps the helpful button, potentially a strong indicator of user's
+  // positive preference on the notification.
+  kHelpful = 1,
+  // The user taps the unhelpful button, potentially a strong indicator of
+  // user's negative preference on the notification.
+  kNotHelpful = 2,
+  // The user clicks the notification.
+  kClick = 3,
+  // The user clicks the body of the notification.
+  kDismiss = 4,
+  // The user has no interaction with the notification for a while.
+  kIgnore = 5,
+  kMaxValue = kIgnore
+};
+
+// The user impression of a particular notification, which may impact the
+// notification display frenquency.
+enum class ImpressionResult {
+  // Invalid user impression.
+  kInvalid = 0,
+  // Positive user impression that the user may like the notification.
+  kPositive = 1,
+  // Positive user impression that the user may dislike the notification.
+  kNegative = 2,
+  // The feedback is neutral to the user.
+  kNeutral = 3,
+  kMaxValue = kNeutral
+};
+
+// Categorizes type of notification buttons. Different type of button clicks
+// may result in change of notification shown frequency.
+enum class ActionButtonType {
+  // The action button is not categorized.
+  kUnknownAction = 0,
+
+  // Helpful button indicates the user likes to interact with the notification.
+  kHelpful = 1,
+
+  // Unhelpful button indicates dislike of the notification.
+  kUnhelpful = 2,
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/public/schedule_params.cc b/chrome/browser/notifications/scheduler/public/schedule_params.cc
new file mode 100644
index 0000000..b926dd1
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/schedule_params.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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/notifications/scheduler/public/schedule_params.h"
+
+namespace notifications {
+
+ScheduleParams::ScheduleParams() : priority(Priority::kLow) {}
+
+bool ScheduleParams::operator==(const ScheduleParams& other) const {
+  return priority == other.priority;
+}
+
+ScheduleParams::~ScheduleParams() = default;
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/schedule_params.h b/chrome/browser/notifications/scheduler/public/schedule_params.h
new file mode 100644
index 0000000..f4ce176
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/schedule_params.h
@@ -0,0 +1,34 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_SCHEDULE_PARAMS_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_SCHEDULE_PARAMS_H_
+
+namespace notifications {
+
+// Specifies when to show the scheduled notification, and throttling details.
+struct ScheduleParams {
+  enum class Priority {
+    // Notification may be delivered if picked by display decision layer. Most
+    // notification types should use this priority.
+    kLow,
+    // Notification may be delivered if picked by display decision layer. Has
+    // higher priority to pick as the next notification to deliver. Should not
+    // be used by feature frequently send notifications.
+    kHigh,
+    // No notification throttling logic is applied, every notification scheduled
+    // will be delivered.
+    kNoThrottle,
+  };
+
+  ScheduleParams();
+  bool operator==(const ScheduleParams& other) const;
+  ~ScheduleParams();
+
+  Priority priority;
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_SCHEDULE_PARAMS_H_
diff --git a/chrome/browser/notifications/scheduler/public/user_action_handler.h b/chrome/browser/notifications/scheduler/public/user_action_handler.h
new file mode 100644
index 0000000..8a6a19f8
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/public/user_action_handler.h
@@ -0,0 +1,39 @@
+// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_USER_ACTION_HANDLER_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_USER_ACTION_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace notifications {
+
+// An interface to plumb user actions events to notification scheduling system.
+// Each event needs to provide an unique id of the notification shown.
+class UserActionHandler {
+ public:
+  // Called when the user clicks on the notification.
+  virtual void OnClick(const std::string& notification_id) = 0;
+
+  // Called when the user clicks on a button on the notification.
+  virtual void OnActionClick(const std::string& notification_id,
+                             ActionButtonType button_type) = 0;
+
+  // Called when the user cancels or dismiss the notification.
+  virtual void OnDismiss(const std::string& notification_id) = 0;
+
+  ~UserActionHandler() = default;
+
+ protected:
+  UserActionHandler() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UserActionHandler);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_USER_ACTION_HANDLER_H_
diff --git a/chrome/browser/notifications/scheduler/schedule_params.cc b/chrome/browser/notifications/scheduler/schedule_params.cc
deleted file mode 100644
index afedfc7b..0000000
--- a/chrome/browser/notifications/scheduler/schedule_params.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/schedule_params.h"
-
-namespace notifications {
-
-ScheduleParams::ScheduleParams() : priority(Priority::kLow) {}
-
-bool ScheduleParams::operator==(const ScheduleParams& other) const {
-  return priority == other.priority;
-}
-
-ScheduleParams::~ScheduleParams() = default;
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/schedule_params.h b/chrome/browser/notifications/scheduler/schedule_params.h
deleted file mode 100644
index f999d55..0000000
--- a/chrome/browser/notifications/scheduler/schedule_params.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_SCHEDULE_PARAMS_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULE_PARAMS_H_
-
-namespace notifications {
-
-// Specifies when to show the scheduled notification, and throttling details.
-struct ScheduleParams {
-  enum class Priority {
-    // Notification may be delivered if picked by display decision layer. Most
-    // notification types should use this priority.
-    kLow,
-    // Notification may be delivered if picked by display decision layer. Has
-    // higher priority to pick as the next notification to deliver. Should not
-    // be used by feature frequently send notifications.
-    kHigh,
-    // No notification throttling logic is applied, every notification scheduled
-    // will be delivered.
-    kNoThrottle,
-  };
-
-  ScheduleParams();
-  bool operator==(const ScheduleParams& other) const;
-  ~ScheduleParams();
-
-  Priority priority;
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULE_PARAMS_H_
diff --git a/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc b/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc
index f9f19a9f..217c7cd 100644
--- a/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc
+++ b/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc
@@ -8,18 +8,18 @@
 
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
-#include "chrome/browser/notifications/scheduler/display_decider.h"
-#include "chrome/browser/notifications/scheduler/icon_store.h"
-#include "chrome/browser/notifications/scheduler/impression_history_tracker.h"
-#include "chrome/browser/notifications/scheduler/impression_store.h"
-#include "chrome/browser/notifications/scheduler/init_aware_scheduler.h"
-#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
-#include "chrome/browser/notifications/scheduler/notification_schedule_service_impl.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_context.h"
-#include "chrome/browser/notifications/scheduler/notification_store.h"
-#include "chrome/browser/notifications/scheduler/scheduled_notification_manager.h"
-#include "chrome/browser/notifications/scheduler/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/internal/display_decider.h"
+#include "chrome/browser/notifications/scheduler/internal/icon_store.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_store.h"
+#include "chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_store.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/leveldb_proto/public/shared_proto_database_client_list.h"
 
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc
deleted file mode 100644
index cd3bdf0..0000000
--- a/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/scheduled_notification_manager.h"
-
-#include <algorithm>
-#include <map>
-
-#include "base/bind.h"
-#include "base/guid.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
-#include "chrome/browser/notifications/scheduler/notification_params.h"
-
-namespace notifications {
-namespace {
-
-// Comparator used to sort notification entries based on creation time.
-bool CreateTimeCompare(const NotificationEntry* lhs,
-                       const NotificationEntry* rhs) {
-  DCHECK(lhs && rhs);
-  return lhs->create_time <= rhs->create_time;
-}
-
-class ScheduledNotificationManagerImpl : public ScheduledNotificationManager {
- public:
-  using Store = std::unique_ptr<CollectionStore<NotificationEntry>>;
-
-  ScheduledNotificationManagerImpl(Store store)
-      : store_(std::move(store)), delegate_(nullptr), weak_ptr_factory_(this) {}
-
- private:
-  void Init(Delegate* delegate, InitCallback callback) override {
-    DCHECK(!delegate_);
-    delegate_ = delegate;
-    store_->InitAndLoad(
-        base::BindOnce(&ScheduledNotificationManagerImpl::OnStoreInitialized,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-  }
-
-  // NotificationManager implementation.
-  void ScheduleNotification(
-      std::unique_ptr<NotificationParams> notification_params) override {
-    DCHECK(notification_params);
-    std::string guid = notification_params->guid;
-    DCHECK(!guid.empty());
-    if (notifications_.find(guid) != notifications_.end()) {
-      // TODO(xingliu): Report duplicate guid failure.
-      return;
-    }
-
-    auto entry =
-        std::make_unique<NotificationEntry>(notification_params->type, guid);
-    entry->notification_data =
-        std::move(notification_params->notification_data);
-    entry->schedule_params = std::move(notification_params->schedule_params);
-    auto* entry_ptr = entry.get();
-    notifications_.emplace(guid, std::move(entry));
-    store_->Add(
-        guid, *entry_ptr,
-        base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationAdded,
-                       weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  void DisplayNotification(const std::string& guid) override {
-    auto it = notifications_.find(guid);
-    if (it == notifications_.end())
-      return;
-
-    // Move the entry to delegate, and delete it from the storage.
-    auto notification_entry = std::move(it->second);
-    notifications_.erase(guid);
-    store_->Delete(
-        guid,
-        base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationDeleted,
-                       weak_ptr_factory_.GetWeakPtr()));
-    if (delegate_)
-      delegate_->DisplayNotification(std::move(notification_entry));
-  }
-
-  void GetAllNotifications(Notifications* notifications) override {
-    DCHECK(notifications);
-
-    for (const auto& pair : notifications_) {
-      const auto& notif = pair.second;
-      DCHECK(notif);
-      (*notifications)[notif->type].emplace_back(notif.get());
-    }
-
-    // Sort by creation time for each notification type.
-    for (auto it = notifications->begin(); it != notifications->end(); ++it) {
-      std::sort(it->second.begin(), it->second.end(), &CreateTimeCompare);
-    }
-  }
-
-  void OnStoreInitialized(InitCallback callback,
-                          bool success,
-                          CollectionStore<NotificationEntry>::Entries entries) {
-    if (!success) {
-      std::move(callback).Run(false);
-      return;
-    }
-
-    for (auto it = entries.begin(); it != entries.end(); ++it) {
-      std::string guid = (*it)->guid;
-      notifications_.emplace(guid, std::move(*it));
-    }
-
-    std::move(callback).Run(true);
-  }
-
-  void OnNotificationAdded(bool success) { NOTIMPLEMENTED(); }
-
-  void OnNotificationDeleted(bool success) { NOTIMPLEMENTED(); }
-
-  Store store_;
-  Delegate* delegate_;
-  std::map<std::string, std::unique_ptr<NotificationEntry>> notifications_;
-
-  base::WeakPtrFactory<ScheduledNotificationManagerImpl> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerImpl);
-};
-
-}  // namespace
-
-// static
-std::unique_ptr<ScheduledNotificationManager>
-ScheduledNotificationManager::Create(
-    std::unique_ptr<CollectionStore<NotificationEntry>> store) {
-  return std::make_unique<ScheduledNotificationManagerImpl>(std::move(store));
-}
-
-ScheduledNotificationManager::ScheduledNotificationManager() = default;
-
-ScheduledNotificationManager::~ScheduledNotificationManager() = default;
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager.h b/chrome/browser/notifications/scheduler/scheduled_notification_manager.h
deleted file mode 100644
index 00d3716..0000000
--- a/chrome/browser/notifications/scheduler/scheduled_notification_manager.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_SCHEDULED_NOTIFICATION_MANAGER_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULED_NOTIFICATION_MANAGER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "chrome/browser/notifications/scheduler/collection_store.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace notifications {
-
-struct NotificationEntry;
-struct NotificationParams;
-
-// Class to manage in-memory scheduled notifications loaded from the storage.
-class ScheduledNotificationManager {
- public:
-  using InitCallback = base::OnceCallback<void(bool)>;
-  using Notifications =
-      std::map<SchedulerClientType, std::vector<const NotificationEntry*>>;
-
-  // Delegate that receives events from the manager.
-  class Delegate {
-   public:
-    // Displays a notification to the user.
-    virtual void DisplayNotification(
-        std::unique_ptr<NotificationEntry> notification) = 0;
-
-    Delegate() = default;
-    virtual ~Delegate() = default;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Delegate);
-  };
-
-  // Creates the instance.
-  static std::unique_ptr<ScheduledNotificationManager> Create(
-      std::unique_ptr<CollectionStore<NotificationEntry>> store);
-
-  // Initializes the notification store.
-  virtual void Init(Delegate* delegate, InitCallback callback) = 0;
-
-  // Adds a new notification.
-  virtual void ScheduleNotification(
-      std::unique_ptr<NotificationParams> notification_params) = 0;
-
-  // Displays a notification, the scheduled notification will be removed from
-  // storage, then Delegate::DisplayNotification() should be invoked.
-  virtual void DisplayNotification(const std::string& guid) = 0;
-
-  // Gets all scheduled notifications. For each type, notifications are sorted
-  // by creation timestamp.
-  virtual void GetAllNotifications(Notifications* notifications) = 0;
-
-  virtual ~ScheduledNotificationManager();
-
- protected:
-  ScheduledNotificationManager();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManager);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULED_NOTIFICATION_MANAGER_H_
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc b/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc
deleted file mode 100644
index 0a6de34..0000000
--- a/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/scheduled_notification_manager.h"
-
-#include "base/bind.h"
-#include "base/guid.h"
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "chrome/browser/notifications/scheduler/collection_store.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
-#include "chrome/browser/notifications/scheduler/notification_params.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using ::testing::Invoke;
-using Entries = std::vector<std::unique_ptr<notifications::NotificationEntry>>;
-
-namespace notifications {
-namespace {
-
-const char kGuid[] = "test_guid_1234";
-const char kTitle[] = "test_title";
-
-NotificationEntry CreateNotificationEntry() {
-  return NotificationEntry(SchedulerClientType::kUnknown, base::GenerateGUID());
-}
-
-class MockDelegate : public ScheduledNotificationManager::Delegate {
- public:
-  MockDelegate() = default;
-  MOCK_METHOD1(DisplayNotification, void(std::unique_ptr<NotificationEntry>));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockDelegate);
-};
-
-class MockNotificationStore : public CollectionStore<NotificationEntry> {
- public:
-  MockNotificationStore() {}
-
-  MOCK_METHOD1(InitAndLoad,
-               void(CollectionStore<NotificationEntry>::LoadCallback));
-  MOCK_METHOD3(Add,
-               void(const std::string&,
-                    const NotificationEntry&,
-                    base::OnceCallback<void(bool)>));
-  MOCK_METHOD3(Update,
-               void(const std::string&,
-                    const NotificationEntry&,
-                    base::OnceCallback<void(bool)>));
-  MOCK_METHOD2(Delete,
-               void(const std::string&, base::OnceCallback<void(bool)>));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockNotificationStore);
-};
-
-class ScheduledNotificationManagerTest : public testing::Test {
- public:
-  ScheduledNotificationManagerTest() : store_(nullptr) {}
-  ~ScheduledNotificationManagerTest() override = default;
-
-  void SetUp() override {
-    delegate_ = std::make_unique<MockDelegate>();
-    auto store = std::make_unique<MockNotificationStore>();
-    store_ = store.get();
-    manager_ = ScheduledNotificationManager::Create(std::move(store));
-  }
-
- protected:
-  ScheduledNotificationManager* manager() { return manager_.get(); }
-  MockNotificationStore* store() { return store_; }
-  MockDelegate* delegate() { return delegate_.get(); }
-
-  // Initializes the manager with predefined data in the store.
-  void InitWithData(std::vector<NotificationEntry> data) {
-    Entries entries;
-    for (auto it = data.begin(); it != data.end(); ++it) {
-      auto entry_ptr = std::make_unique<NotificationEntry>(
-          SchedulerClientType::kUnknown, it->guid);
-      *(entry_ptr.get()) = *it;
-      entries.emplace_back(std::move(entry_ptr));
-    }
-
-    // Initialize the store and call the callback.
-    EXPECT_CALL(*store(), InitAndLoad(_))
-        .WillOnce(
-            Invoke([&entries](base::OnceCallback<void(bool, Entries)> cb) {
-              std::move(cb).Run(true, std::move(entries));
-            }));
-    base::RunLoop loop;
-    manager()->Init(delegate(),
-                    base::BindOnce(
-                        [](base::RepeatingClosure closure, bool success) {
-                          EXPECT_TRUE(success);
-                          std::move(closure).Run();
-                        },
-                        loop.QuitClosure()));
-    loop.Run();
-  }
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  std::unique_ptr<MockDelegate> delegate_;
-  MockNotificationStore* store_;
-  std::unique_ptr<ScheduledNotificationManager> manager_;
-  DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerTest);
-};
-
-// Verify that error is received when initialization failed.
-TEST_F(ScheduledNotificationManagerTest, InitFailed) {
-  EXPECT_CALL(*store(), InitAndLoad(_))
-      .WillOnce(Invoke([](base::OnceCallback<void(bool, Entries)> cb) {
-        std::move(cb).Run(false, Entries());
-      }));
-
-  base::RunLoop loop;
-  manager()->Init(delegate(),
-                  base::BindOnce(
-                      [](base::RepeatingClosure closure, bool success) {
-                        // Expected to receive error.
-                        EXPECT_FALSE(success);
-                        std::move(closure).Run();
-                      },
-                      loop.QuitClosure()));
-  loop.Run();
-}
-
-// Test to schedule a notification.
-TEST_F(ScheduledNotificationManagerTest, ScheduleNotification) {
-  InitWithData(std::vector<NotificationEntry>());
-  NotificationData notification_data;
-  notification_data.title = kTitle;
-  ScheduleParams schedule_params;
-  schedule_params.priority = ScheduleParams::Priority::kHigh;
-  auto params = std::make_unique<NotificationParams>(
-      SchedulerClientType::kUnknown, notification_data, schedule_params);
-  std::string guid = params->guid;
-  EXPECT_FALSE(guid.empty());
-
-  // Verify call contract.
-  EXPECT_CALL(*store(), Add(guid, _, _));
-  manager()->ScheduleNotification(std::move(params));
-
-  // Verify in-memory data.
-  ScheduledNotificationManager::Notifications notifications;
-  manager()->GetAllNotifications(&notifications);
-  EXPECT_EQ(notifications.size(), 1u);
-  const NotificationEntry* entry = *(notifications.begin()->second.begin());
-  EXPECT_EQ(entry->guid, guid);
-  EXPECT_NE(entry->create_time, base::Time());
-
-  // TODO(xingliu): change these to compare with operator==.
-  EXPECT_EQ(entry->notification_data.title, kTitle);
-  EXPECT_EQ(entry->schedule_params.priority, ScheduleParams::Priority::kHigh);
-}
-
-// Test to schedule a notification without guid, we will auto generated one.
-TEST_F(ScheduledNotificationManagerTest, ScheduleNotificationEmptyGuid) {
-  InitWithData(std::vector<NotificationEntry>());
-  auto params = std::make_unique<NotificationParams>(
-      SchedulerClientType::kUnknown, NotificationData(), ScheduleParams());
-
-  // Verify call contract.
-  EXPECT_CALL(*store(), Add(_, _, _));
-  manager()->ScheduleNotification(std::move(params));
-
-  // Verify in-memory data.
-  ScheduledNotificationManager::Notifications notifications;
-  manager()->GetAllNotifications(&notifications);
-  EXPECT_EQ(notifications.size(), 1u);
-  const NotificationEntry* entry = *(notifications.begin()->second.begin());
-  EXPECT_NE(entry->guid, std::string());
-  EXPECT_NE(entry->create_time, base::Time());
-}
-
-// TODO(xingliu): change this to compare with operator==.
-MATCHER_P(NotificationEntryIs, expected, "") {
-  return arg->guid == expected.guid;
-}
-
-// Test to display a notification.
-TEST_F(ScheduledNotificationManagerTest, DisplayNotification) {
-  auto entry = CreateNotificationEntry();
-  entry.guid = kGuid;
-  InitWithData(std::vector<NotificationEntry>({entry}));
-
-  // Verify delegate and dependency call contract.
-  EXPECT_CALL(*store(), Delete(kGuid, _));
-  EXPECT_CALL(*delegate(), DisplayNotification(NotificationEntryIs(entry)));
-  manager()->DisplayNotification(kGuid);
-
-  // Verify in-memory data.
-  ScheduledNotificationManager::Notifications notifications;
-  manager()->GetAllNotifications(&notifications);
-  EXPECT_TRUE(notifications.empty());
-}
-
-// Verify GetAllNotifications API, the notification should be sorted based on
-// creation timestamp.
-TEST_F(ScheduledNotificationManagerTest, GetAllNotifications) {
-  // Ordering: entry1.create_time < entry0.create_time < entry2.create_time.
-  auto now = base::Time::Now();
-  auto entry0 = CreateNotificationEntry();
-  entry0.create_time = now;
-  auto entry1 = CreateNotificationEntry();
-  entry1.create_time = now - base::TimeDelta::FromMinutes(1);
-  auto entry2 = CreateNotificationEntry();
-  entry2.create_time = now + base::TimeDelta::FromMinutes(1);
-
-  InitWithData(std::vector<NotificationEntry>({entry0, entry1, entry2}));
-  ScheduledNotificationManager::Notifications notifications;
-  manager()->GetAllNotifications(&notifications);
-  EXPECT_EQ(notifications.size(), 1u);
-  EXPECT_EQ(notifications.begin()->second.size(), 3u);
-
-  // Entries returned by GetAllNotifications() should be sorted by creation
-  // timestamp.
-  const NotificationEntry* output0 = notifications.begin()->second.front();
-  const NotificationEntry* output2 = notifications.begin()->second.back();
-  EXPECT_EQ(output0->create_time, entry1.create_time);
-  EXPECT_EQ(output2->create_time, entry2.create_time);
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduler_config.cc b/chrome/browser/notifications/scheduler/scheduler_config.cc
deleted file mode 100644
index 7ded6d5..0000000
--- a/chrome/browser/notifications/scheduler/scheduler_config.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/scheduler_config.h"
-
-namespace notifications {
-
-// The impression history is hold for 4 weeks.
-constexpr base::TimeDelta kDefaultImpressionExpiration =
-    base::TimeDelta::FromDays(28);
-
-// The suppression lasts 8 weeks.
-constexpr base::TimeDelta kDefaultSuppressionDuration =
-    base::TimeDelta::FromDays(56);
-
-// The morning task by default will run at 7am.
-constexpr int kDefaultMorningTaskHour = 7;
-
-// The evening task by default will run at 6pm.
-constexpr int kDefaultEveningTaskHour = 18;
-
-// Check consecutive notification dismisses in this duration to generate a
-// dismiss event.
-constexpr base::TimeDelta kDefaultDimissDuration = base::TimeDelta::FromDays(7);
-
-// Default background task time window duration.
-constexpr base::TimeDelta kDefaultBackgroundTaskWindowDuration =
-    base::TimeDelta::FromHours(1);
-
-// static
-std::unique_ptr<SchedulerConfig> SchedulerConfig::Create() {
-  return std::make_unique<SchedulerConfig>();
-}
-
-SchedulerConfig::SchedulerConfig()
-    : max_daily_shown_all_type(3),
-      max_daily_shown_per_type(10),
-      impression_expiration(kDefaultImpressionExpiration),
-      suppression_duration(kDefaultSuppressionDuration),
-      dismiss_count(3),
-      dismiss_duration(kDefaultDimissDuration),
-      morning_task_hour(kDefaultMorningTaskHour),
-      evening_task_hour(kDefaultEveningTaskHour),
-      background_task_window_duration(kDefaultBackgroundTaskWindowDuration) {
-  // TODO(xingliu): Add constructor using finch data.
-}
-
-SchedulerConfig::~SchedulerConfig() = default;
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduler_config.h b/chrome/browser/notifications/scheduler/scheduler_config.h
deleted file mode 100644
index 415b1b0d..0000000
--- a/chrome/browser/notifications/scheduler/scheduler_config.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_SCHEDULER_CONFIG_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_CONFIG_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-
-namespace notifications {
-
-// Configuration of notification scheduler system.
-struct SchedulerConfig {
-  // Creates a default scheduler config.
-  static std::unique_ptr<SchedulerConfig> Create();
-
-  SchedulerConfig();
-  ~SchedulerConfig();
-
-  // Maximum number of all types of notifications shown to the user per day.
-  int max_daily_shown_all_type;
-
-  // Maximum number of notifications shown to the user per day for each type.
-  int max_daily_shown_per_type;
-
-  // The time for a notification impression history data to expire. The
-  // impression history will be deleted then.
-  base::TimeDelta impression_expiration;
-
-  // Duration of suppression when negative impression is applied.
-  base::TimeDelta suppression_duration;
-
-  // The number of consecutive notification dismisses to generate a dismiss
-  // event.
-  int dismiss_count;
-
-  // Used to check whether |dismiss_count| consecutive notification dimisses are
-  // in this duration, to generate a dismiss event.
-  base::TimeDelta dismiss_duration;
-
-  // The hour (from 0 to 23) to run the morning background task for notification
-  // scheduler.
-  int morning_task_hour;
-
-  // The hour (from 0 to 23) to run the evening background task for notification
-  // scheduler.
-  int evening_task_hour;
-
-  // The time window to launch the background task.
-  base::TimeDelta background_task_window_duration;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SchedulerConfig);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_CONFIG_H_
diff --git a/chrome/browser/notifications/scheduler/scheduler_utils.cc b/chrome/browser/notifications/scheduler/scheduler_utils.cc
deleted file mode 100644
index f2a1d71..0000000
--- a/chrome/browser/notifications/scheduler/scheduler_utils.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/scheduler_utils.h"
-
-#include "chrome/browser/notifications/scheduler/impression_types.h"
-
-namespace notifications {
-
-bool ToLocalHour(int hour,
-                 const base::Time& today,
-                 int day_delta,
-                 base::Time* out) {
-  DCHECK_GE(hour, 0);
-  DCHECK_LE(hour, 23);
-  DCHECK(out);
-
-  // Gets the local time at |hour| in yesterday.
-  base::Time another_day = today + base::TimeDelta::FromDays(day_delta);
-  base::Time::Exploded another_day_exploded;
-  another_day.LocalExplode(&another_day_exploded);
-  another_day_exploded.hour = hour;
-  another_day_exploded.minute = 0;
-  another_day_exploded.second = 0;
-  another_day_exploded.millisecond = 0;
-
-  // Converts local exploded time to time stamp.
-  return base::Time::FromLocalExploded(another_day_exploded, out);
-}
-
-void NotificationsShownToday(
-    const std::map<SchedulerClientType, const ClientState*>& client_states,
-    std::map<SchedulerClientType, int>* shown_per_type,
-    int* shown_total,
-    SchedulerClientType* last_shown_type) {
-  base::Time last_shown_time;
-  base::Time now(base::Time::Now());
-  base::Time beginning_of_today;
-  bool success = ToLocalHour(0, now, 0, &beginning_of_today);
-  DCHECK(success);
-
-  for (const auto& state : client_states) {
-    const auto* client_state = state.second;
-    for (const auto& impression : client_state->impressions) {
-      // Tracks last notification shown to the user.
-      if (impression.create_time > last_shown_time) {
-        last_shown_time = impression.create_time;
-        *last_shown_type = client_state->type;
-      }
-
-      // Count notification shown today.
-      if (impression.create_time >= beginning_of_today) {
-        (*shown_per_type)[client_state->type]++;
-        ++(*shown_total);
-      }
-    }
-  }
-}
-
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduler_utils.h b/chrome/browser/notifications/scheduler/scheduler_utils.h
deleted file mode 100644
index cee2533d..0000000
--- a/chrome/browser/notifications/scheduler/scheduler_utils.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_SCHEDULER_UTILS_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_UTILS_H_
-
-#include <map>
-
-#include "base/time/time.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
-
-namespace notifications {
-
-struct ClientState;
-
-// Retrieves the time stamp of a certain hour at a certain day from today.
-// |hour| must be in the range of [0, 23].
-// |today| is a timestamp to define today, usually caller can directly pass in
-// the current system time.
-// |day_delta| is the different between the output date and today.
-// Returns false if the conversion is failed.
-bool ToLocalHour(int hour,
-                 const base::Time& today,
-                 int day_delta,
-                 base::Time* out);
-
-// Calculates the notifications shown today from impression data.
-void NotificationsShownToday(
-    const std::map<SchedulerClientType, const ClientState*>& client_states,
-    std::map<SchedulerClientType, int>* shown_per_type,
-    int* shown_total,
-    SchedulerClientType* last_shown_type);
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_UTILS_H_
diff --git a/chrome/browser/notifications/scheduler/scheduler_utils_unittest.cc b/chrome/browser/notifications/scheduler/scheduler_utils_unittest.cc
deleted file mode 100644
index e4acaee..0000000
--- a/chrome/browser/notifications/scheduler/scheduler_utils_unittest.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 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/notifications/scheduler/scheduler_utils.h"
-
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace notifications {
-namespace {
-
-// Verifies we can get the correct time stamp at certain hour in yesterday.
-TEST(SchedulerUtilsTest, ToLocalHour) {
-  base::Time today, another_day, expected;
-
-  // Timestamp of another day in the past.
-  EXPECT_TRUE(base::Time::FromString("10/15/07 12:45:12 PM", &today));
-  EXPECT_TRUE(ToLocalHour(6, today, -1, &another_day));
-  EXPECT_TRUE(base::Time::FromString("10/14/07 06:00:00 AM", &expected));
-  EXPECT_EQ(expected, another_day);
-
-  EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &today));
-  EXPECT_TRUE(ToLocalHour(0, today, -1, &another_day));
-  EXPECT_TRUE(base::Time::FromString("03/24/19 00:00:00 AM", &expected));
-  EXPECT_EQ(expected, another_day);
-
-  // Timestamp of the same day.
-  EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &today));
-  EXPECT_TRUE(ToLocalHour(0, today, 0, &another_day));
-  EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &expected));
-  EXPECT_EQ(expected, another_day);
-
-  // Timestamp of another day in the future.
-  EXPECT_TRUE(base::Time::FromString("03/25/19 06:35:27 AM", &today));
-  EXPECT_TRUE(ToLocalHour(16, today, 7, &another_day));
-  EXPECT_TRUE(base::Time::FromString("04/01/19 16:00:00 PM", &expected));
-  EXPECT_EQ(expected, another_day);
-}
-
-}  // namespace
-}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/test/BUILD.gn b/chrome/browser/notifications/scheduler/test/BUILD.gn
index ac37ed5..27f61b4 100644
--- a/chrome/browser/notifications/scheduler/test/BUILD.gn
+++ b/chrome/browser/notifications/scheduler/test/BUILD.gn
@@ -9,7 +9,8 @@
 }
 
 source_set("test_lib") {
-  visibility = [ "//chrome/browser/notifications/scheduler:unit_tests" ]
+  visibility =
+      [ "//chrome/browser/notifications/scheduler/internal:unit_tests" ]
 
   sources = [
     "test_utils.cc",
@@ -18,7 +19,7 @@
 
   deps = [
     "//base",
-    "//chrome/browser/notifications/scheduler:lib",
-    "//chrome/browser/notifications/scheduler:public",
+    "//chrome/browser/notifications/scheduler/internal:lib",
+    "//chrome/browser/notifications/scheduler/public",
   ]
 }
diff --git a/chrome/browser/notifications/scheduler/test/test_utils.cc b/chrome/browser/notifications/scheduler/test/test_utils.cc
index ca00df6..760d7f1 100644
--- a/chrome/browser/notifications/scheduler/test/test_utils.cc
+++ b/chrome/browser/notifications/scheduler/test/test_utils.cc
@@ -7,8 +7,8 @@
 #include <sstream>
 #include <utility>
 
-#include "chrome/browser/notifications/scheduler/notification_data.h"
-#include "chrome/browser/notifications/scheduler/notification_entry.h"
+#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
+#include "chrome/browser/notifications/scheduler/public/notification_data.h"
 
 namespace notifications {
 namespace test {
diff --git a/chrome/browser/notifications/scheduler/test/test_utils.h b/chrome/browser/notifications/scheduler/test/test_utils.h
index 9ef1a99..da79028 100644
--- a/chrome/browser/notifications/scheduler/test/test_utils.h
+++ b/chrome/browser/notifications/scheduler/test/test_utils.h
@@ -9,9 +9,9 @@
 #include <string>
 #include <vector>
 
-#include "chrome/browser/notifications/scheduler/impression_history_tracker.h"
-#include "chrome/browser/notifications/scheduler/impression_types.h"
-#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h"
+#include "chrome/browser/notifications/scheduler/internal/impression_types.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
 
 namespace notifications {
 
diff --git a/chrome/browser/notifications/scheduler/user_action_handler.h b/chrome/browser/notifications/scheduler/user_action_handler.h
deleted file mode 100644
index d06b7fc..0000000
--- a/chrome/browser/notifications/scheduler/user_action_handler.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_USER_ACTION_HANDLER_H_
-#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_USER_ACTION_HANDLER_H_
-
-#include <string>
-
-#include "base/macros.h"
-
-namespace notifications {
-
-// An interface to plumb user actions events to notification scheduling system.
-// Each event needs to provide an unique id of the notification shown.
-class UserActionHandler {
- public:
-  // Called when the user clicks on the notification.
-  virtual void OnClick(const std::string& notification_id) = 0;
-
-  // Called when the user clicks on a button on the notification.
-  virtual void OnActionClick(const std::string& notification_id,
-                             ActionButtonType button_type) = 0;
-
-  // Called when the user cancels or dismiss the notification.
-  virtual void OnDismiss(const std::string& notification_id) = 0;
-
-  ~UserActionHandler() = default;
-
- protected:
-  UserActionHandler() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(UserActionHandler);
-};
-
-}  // namespace notifications
-
-#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_USER_ACTION_HANDLER_H_
diff --git a/chrome/browser/performance_manager/DEPS b/chrome/browser/performance_manager/DEPS
new file mode 100644
index 0000000..86d5863
--- /dev/null
+++ b/chrome/browser/performance_manager/DEPS
@@ -0,0 +1,15 @@
+# Prevent the include of Chrome specific files in general.
+include_rules = [
+  "-chrome/browser",
+  "+chrome/browser/performance_manager",
+]
+
+# Allow some Chrome specific files to includes more things from //chrome.
+specific_include_rules = {
+  "chrome.*": [
+    "+chrome/browser",
+  ],
+  "webui_graph_dump_impl\.cc": [
+    "+chrome/browser",
+  ],
+}
diff --git a/chrome/browser/performance_manager/observers/isolation_context_metrics.cc b/chrome/browser/performance_manager/observers/isolation_context_metrics.cc
index cde8d73..4476fa17 100644
--- a/chrome/browser/performance_manager/observers/isolation_context_metrics.cc
+++ b/chrome/browser/performance_manager/observers/isolation_context_metrics.cc
@@ -80,11 +80,11 @@
 
 // static
 const char IsolationContextMetrics::kProcessDataByTimeHistogramName[] =
-    "PerformanceManager.FrameSiteInstanceProcessRelationship.ByTime";
+    "PerformanceManager.FrameSiteInstanceProcessRelationship.ByTime2";
 
 // static
 const char IsolationContextMetrics::kProcessDataByProcessHistogramName[] =
-    "PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess";
+    "PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess2";
 
 // static
 const char
@@ -160,10 +160,12 @@
     // exist for renderer processes that ever actually hosted frames.
     const auto* process_node = ProcessNodeImpl::FromNodeBase(node);
     if (auto* process_data = ProcessData::Get(process_node)) {
-      const auto state =
-          process_data->has_hosted_multiple_frames_with_same_site_instance
-              ? ProcessDataState::kSomeFramesHaveSameSiteInstance
-              : ProcessDataState::kAllFramesHaveDistinctSiteInstances;
+      auto state = ProcessDataState::kOnlyOneFrameExists;
+      if (process_data->has_hosted_multiple_frames) {
+        state = process_data->has_hosted_multiple_frames_with_same_site_instance
+                    ? ProcessDataState::kSomeFramesHaveSameSiteInstance
+                    : ProcessDataState::kAllFramesHaveDistinctSiteInstances;
+      }
       UMA_HISTOGRAM_ENUMERATION(kProcessDataByProcessHistogramName, state);
     }
   }
@@ -227,8 +229,11 @@
   if (process_data->site_instance_frame_count.empty())
     return ProcessDataState::kUndefined;
 
-  if (process_data->multi_frame_site_instance_count == 0)
+  if (process_data->multi_frame_site_instance_count == 0) {
+    if (process_data->site_instance_frame_count.size() == 1)
+      return ProcessDataState::kOnlyOneFrameExists;
     return ProcessDataState::kAllFramesHaveDistinctSiteInstances;
+  }
 
   return ProcessDataState::kSomeFramesHaveSameSiteInstance;
 }
@@ -258,6 +263,13 @@
   auto* data = ProcessData::GetOrCreate(process_node);
   const auto old_state = GetProcessDataState(data);
 
+  if (delta == 1) {
+    if (++data->frame_count > 1)
+      data->has_hosted_multiple_frames = true;
+  } else {
+    --data->frame_count;
+  }
+
   auto iter = data->site_instance_frame_count
                   .insert(std::make_pair(frame_node->site_instance_id(), 0))
                   .first;
diff --git a/chrome/browser/performance_manager/observers/isolation_context_metrics.h b/chrome/browser/performance_manager/observers/isolation_context_metrics.h
index 60d6cf2..e64a5257 100644
--- a/chrome/browser/performance_manager/observers/isolation_context_metrics.h
+++ b/chrome/browser/performance_manager/observers/isolation_context_metrics.h
@@ -75,10 +75,14 @@
     // instance in the process. This is typically small for most processes, but
     // can go to O(100s) for power users hence the use of small_map.
     base::small_map<std::unordered_map<int32_t, int>> site_instance_frame_count;
+    // The number of frames in this process.
+    int frame_count = 0;
     // The number of site instances with multiple frames in this process.
     // Basically, this counts the number of entries in
     // |site_instance_frame_count| that are > 1.
     int multi_frame_site_instance_count = 0;
+    // Whether or not this process has *ever* hosted multiple frames.
+    bool has_hosted_multiple_frames = false;
     // Whether or not this process has *ever* hosted multiple frames in the same
     // site instance. This goes to true if |multi_frame_site_instance_count| is
     // ever greater than 0.
@@ -94,8 +98,9 @@
     kUndefined = -1,  // This value is never reported, but used in logic.
     kAllFramesHaveDistinctSiteInstances = 0,
     kSomeFramesHaveSameSiteInstance = 1,
+    kOnlyOneFrameExists = 2,
     // Must be maintained as the max value.
-    kMaxValue = kSomeFramesHaveSameSiteInstance
+    kMaxValue = kOnlyOneFrameExists
   };
 
   // Tracks summary information regarding pages in a browsing instance.
diff --git a/chrome/browser/performance_manager/observers/isolation_context_metrics_unittest.cc b/chrome/browser/performance_manager/observers/isolation_context_metrics_unittest.cc
index 67398e2..8b5d3e6 100644
--- a/chrome/browser/performance_manager/observers/isolation_context_metrics_unittest.cc
+++ b/chrome/browser/performance_manager/observers/isolation_context_metrics_unittest.cc
@@ -150,7 +150,7 @@
   // Make up a site instance with one frame.
   data.site_instance_frame_count[kSID1] = 1;
   EXPECT_EQ(1u, data.site_instance_frame_count.size());
-  EXPECT_EQ(ProcessDataState::kAllFramesHaveDistinctSiteInstances,
+  EXPECT_EQ(ProcessDataState::kOnlyOneFrameExists,
             TestIsolationContextMetrics::GetProcessDataState(&data));
 
   // Make up another site instance with one frame.
@@ -191,7 +191,7 @@
   // Erase the first site instance.
   data.site_instance_frame_count.erase(kSID1);
   EXPECT_EQ(1u, data.site_instance_frame_count.size());
-  EXPECT_EQ(ProcessDataState::kAllFramesHaveDistinctSiteInstances,
+  EXPECT_EQ(ProcessDataState::kOnlyOneFrameExists,
             TestIsolationContextMetrics::GetProcessDataState(&data));
 }
 
@@ -210,10 +210,12 @@
   // Expect the ProcessData to exist and be correctly filled out.
   auto* data1 = ProcessData::GetOrCreate(process.get());
   EXPECT_EQ(1u, data1->site_instance_frame_count.size());
+  EXPECT_EQ(1, data1->frame_count);
   EXPECT_EQ(0, data1->multi_frame_site_instance_count);
+  EXPECT_FALSE(data1->has_hosted_multiple_frames);
   EXPECT_FALSE(data1->has_hosted_multiple_frames_with_same_site_instance);
   EXPECT_EQ(task_env().NowTicks(), data1->last_reported);
-  EXPECT_EQ(ProcessDataState::kAllFramesHaveDistinctSiteInstances,
+  EXPECT_EQ(ProcessDataState::kOnlyOneFrameExists,
             TestIsolationContextMetrics::GetProcessDataState(data1));
 
   // Expect no metrics to have been emitted.
@@ -228,28 +230,30 @@
   EXPECT_EQ(task_env().NowTicks(), data1->last_reported);
   histogram_tester_.ExpectUniqueSample(
       metrics_->kProcessDataByTimeHistogramName,
-      ProcessDataState::kAllFramesHaveDistinctSiteInstances,
+      ProcessDataState::kOnlyOneFrameExists,
       metrics_->kReportingInterval.InSeconds());
   histogram_tester_.ExpectTotalCount(
       metrics_->kProcessDataByProcessHistogramName, 0);
 
   {
-    // Advance time and add another frame to the same site instance, as a child
+    // Advance time and add another frame to a new site instance, as a child
     // of |frame1|.
     task_env().FastForwardBy(base::TimeDelta::FromSeconds(1));
     auto frame2 =
-        CreateFrameNode(process.get(), page.get(), kBID1, kSID1, frame1.get());
-    EXPECT_EQ(1u, data1->site_instance_frame_count.size());
-    EXPECT_EQ(1, data1->multi_frame_site_instance_count);
-    EXPECT_TRUE(data1->has_hosted_multiple_frames_with_same_site_instance);
-    EXPECT_EQ(ProcessDataState::kSomeFramesHaveSameSiteInstance,
+        CreateFrameNode(process.get(), page.get(), kBID1, kSID2, frame1.get());
+    EXPECT_EQ(2u, data1->site_instance_frame_count.size());
+    EXPECT_EQ(2, data1->frame_count);
+    EXPECT_EQ(0, data1->multi_frame_site_instance_count);
+    EXPECT_TRUE(data1->has_hosted_multiple_frames);
+    EXPECT_FALSE(data1->has_hosted_multiple_frames_with_same_site_instance);
+    EXPECT_EQ(ProcessDataState::kAllFramesHaveDistinctSiteInstances,
               TestIsolationContextMetrics::GetProcessDataState(data1));
 
     // Expect metrics to have been reported on the state change.
     EXPECT_EQ(task_env().NowTicks(), data1->last_reported);
     histogram_tester_.ExpectUniqueSample(
         metrics_->kProcessDataByTimeHistogramName,
-        ProcessDataState::kAllFramesHaveDistinctSiteInstances,
+        ProcessDataState::kOnlyOneFrameExists,
         metrics_->kReportingInterval.InSeconds() + 1);
     histogram_tester_.ExpectTotalCount(
         metrics_->kProcessDataByProcessHistogramName, 0);
@@ -266,11 +270,63 @@
       metrics_->kReportingInterval.InSeconds() + 2);
   histogram_tester_.ExpectBucketCount(
       metrics_->kProcessDataByTimeHistogramName,
-      ProcessDataState::kAllFramesHaveDistinctSiteInstances,
+      ProcessDataState::kAllFramesHaveDistinctSiteInstances, 1);
+  histogram_tester_.ExpectBucketCount(
+      metrics_->kProcessDataByTimeHistogramName,
+      ProcessDataState::kOnlyOneFrameExists,
       metrics_->kReportingInterval.InSeconds() + 1);
+  histogram_tester_.ExpectTotalCount(
+      metrics_->kProcessDataByProcessHistogramName, 0);
+
+  {
+    // Advance time and add another frame to the same site instance, as a child
+    // of |frame1|.
+    task_env().FastForwardBy(base::TimeDelta::FromSeconds(1));
+    auto frame2 =
+        CreateFrameNode(process.get(), page.get(), kBID1, kSID1, frame1.get());
+    EXPECT_EQ(1u, data1->site_instance_frame_count.size());
+    EXPECT_EQ(2, data1->frame_count);
+    EXPECT_EQ(1, data1->multi_frame_site_instance_count);
+    EXPECT_TRUE(data1->has_hosted_multiple_frames);
+    EXPECT_TRUE(data1->has_hosted_multiple_frames_with_same_site_instance);
+    EXPECT_EQ(ProcessDataState::kSomeFramesHaveSameSiteInstance,
+              TestIsolationContextMetrics::GetProcessDataState(data1));
+
+    // Expect metrics to have been reported on the state change.
+    EXPECT_EQ(task_env().NowTicks(), data1->last_reported);
+    histogram_tester_.ExpectTotalCount(
+        metrics_->kProcessDataByTimeHistogramName,
+        metrics_->kReportingInterval.InSeconds() + 3);
+    histogram_tester_.ExpectBucketCount(
+        metrics_->kProcessDataByTimeHistogramName,
+        ProcessDataState::kAllFramesHaveDistinctSiteInstances, 1);
+    histogram_tester_.ExpectBucketCount(
+        metrics_->kProcessDataByTimeHistogramName,
+        ProcessDataState::kOnlyOneFrameExists,
+        metrics_->kReportingInterval.InSeconds() + 2);
+    histogram_tester_.ExpectTotalCount(
+        metrics_->kProcessDataByProcessHistogramName, 0);
+
+    // Advance time.
+    task_env().FastForwardBy(base::TimeDelta::FromSeconds(1));
+  }
+
+  // The second frame will be destroyed as it goes out of scope. Expect another
+  // flush of metrics.
+  EXPECT_EQ(task_env().NowTicks(), data1->last_reported);
+  histogram_tester_.ExpectTotalCount(
+      metrics_->kProcessDataByTimeHistogramName,
+      metrics_->kReportingInterval.InSeconds() + 4);
+  histogram_tester_.ExpectBucketCount(
+      metrics_->kProcessDataByTimeHistogramName,
+      ProcessDataState::kAllFramesHaveDistinctSiteInstances, 1);
   histogram_tester_.ExpectBucketCount(
       metrics_->kProcessDataByTimeHistogramName,
       ProcessDataState::kSomeFramesHaveSameSiteInstance, 1);
+  histogram_tester_.ExpectBucketCount(
+      metrics_->kProcessDataByTimeHistogramName,
+      ProcessDataState::kOnlyOneFrameExists,
+      metrics_->kReportingInterval.InSeconds() + 2);
   histogram_tester_.ExpectTotalCount(
       metrics_->kProcessDataByProcessHistogramName, 0);
 
diff --git a/chrome/browser/performance_manager/public/DEPS b/chrome/browser/performance_manager/public/DEPS
new file mode 100644
index 0000000..a35d1e2
--- /dev/null
+++ b/chrome/browser/performance_manager/public/DEPS
@@ -0,0 +1,5 @@
+# The public includes shouldn't depend on anything not public.
+include_rules = [
+  "-chrome/browser/performance_manager",
+  "+chrome/browser/performance_manager/public",
+]
\ No newline at end of file
diff --git a/chrome/browser/printing/print_dialog_cloud.cc b/chrome/browser/printing/print_dialog_cloud.cc
index b3c70939..e224495 100644
--- a/chrome/browser/printing/print_dialog_cloud.cc
+++ b/chrome/browser/printing/print_dialog_cloud.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/printing/print_dialog_cloud.h"
 
+#include "base/bind.h"
 #include "base/macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/browser_process.h"
@@ -27,8 +28,10 @@
 
 class SignInObserver : public content::WebContentsObserver {
  public:
-  explicit SignInObserver(content::WebContents* web_contents)
-      : WebContentsObserver(web_contents), weak_ptr_factory_(this) {}
+  SignInObserver(content::WebContents* web_contents, base::OnceClosure callback)
+      : WebContentsObserver(web_contents),
+        callback_(std::move(callback)),
+        weak_ptr_factory_(this) {}
 
  private:
   // Overridden from content::WebContentsObserver:
@@ -49,10 +52,12 @@
   void WebContentsDestroyed() override { delete this; }
 
   void OnSignIn() {
+    std::move(callback_).Run();
     if (web_contents())
       web_contents()->Close();
   }
 
+  base::OnceClosure callback_;
   base::WeakPtrFactory<SignInObserver> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SignInObserver);
@@ -60,7 +65,9 @@
 
 }  // namespace
 
-void CreateCloudPrintSigninTab(Browser* browser, bool add_account) {
+void CreateCloudPrintSigninTab(Browser* browser,
+                               bool add_account,
+                               base::OnceClosure callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (AccountConsistencyModeManager::IsMirrorEnabledForProfile(
           browser->profile())) {
@@ -79,7 +86,7 @@
             content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
             ui::PAGE_TRANSITION_AUTO_BOOKMARK, false));
     // This observer will delete itself after destroying the WebContents.
-    new SignInObserver(web_contents);
+    new SignInObserver(web_contents, std::move(callback));
   }
 }
 
diff --git a/chrome/browser/printing/print_dialog_cloud.h b/chrome/browser/printing/print_dialog_cloud.h
index c94f7fc..25de632 100644
--- a/chrome/browser/printing/print_dialog_cloud.h
+++ b/chrome/browser/printing/print_dialog_cloud.h
@@ -7,6 +7,8 @@
 
 #include <string>
 
+#include "base/callback_forward.h"
+
 class Browser;
 class Profile;
 
@@ -18,7 +20,9 @@
 
 // Creates a tab with Google 'sign in' or 'add account' page, based on
 // passed |add_account| value.
-void CreateCloudPrintSigninTab(Browser* browser, bool add_account);
+void CreateCloudPrintSigninTab(Browser* browser,
+                               bool add_account,
+                               base::OnceClosure callback);
 
 // Parse switches from command_line and display the print dialog as appropriate.
 bool CreatePrintDialogFromCommandLine(Profile* profile,
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index eeec06d..44b6ff8 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -503,7 +503,7 @@
 
 // GetEndpoint method ----------------------------------------------------------
 
-GURL PushMessagingServiceImpl::GetEndpoint(bool standard_protocol) const {
+GURL PushMessagingServiceImpl::GetEndpoint(bool standard_protocol) {
   return GURL(kPushMessagingGcmEndpoint);
 }
 
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index 9e75f64a..20f1b54 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -91,7 +91,7 @@
   bool CanHandle(const std::string& app_id) const override;
 
   // content::PushMessagingService implementation:
-  GURL GetEndpoint(bool standard_protocol) const override;
+  GURL GetEndpoint(bool standard_protocol) override;
   void SubscribeFromDocument(const GURL& requesting_origin,
                              int64_t service_worker_registration_id,
                              int renderer_id,
diff --git a/chrome/browser/resource_coordinator/DEPS b/chrome/browser/resource_coordinator/DEPS
index aa42298..b214e26 100644
--- a/chrome/browser/resource_coordinator/DEPS
+++ b/chrome/browser/resource_coordinator/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
+  # resource_coordinator can include files from performance_manager.
+  "+chrome/browser/performance_manager",
+
   "+services/resource_coordinator/public",
   # No inclusion of WebKit from the browser, other than strictly enum/POD,
   # header-only types, and some selected common code.
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/bg.js b/chrome/browser/resources/chromeos/kiosk_next_home/bg.js
index 15de25dd..416a4d0 100644
--- a/chrome/browser/resources/chromeos/kiosk_next_home/bg.js
+++ b/chrome/browser/resources/chromeos/kiosk_next_home/bg.js
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 chrome.app.runtime.onLaunched.addListener(() => {
-  const windowOptions = {id: 'main', state: 'maximized', frame: 'none'};
-  chrome.app.window.create('main.html', windowOptions);
+  const windowOptions = {state: 'maximized', frame: 'none'};
+  chrome.app.window.create('main.html', windowOptions, (newWindow) => {
+    newWindow.maximize();
+  });
 });
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/bg_internal.js b/chrome/browser/resources/chromeos/kiosk_next_home/bg_internal.js
index b2ab628..ec93f75 100644
--- a/chrome/browser/resources/chromeos/kiosk_next_home/bg_internal.js
+++ b/chrome/browser/resources/chromeos/kiosk_next_home/bg_internal.js
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 chrome.app.runtime.onLaunched.addListener(() => {
-  const windowOptions = {id: 'main', state: 'maximized', frame: 'none'};
-  chrome.app.window.create('internal/main.html', windowOptions);
+  const windowOptions = {state: 'maximized', frame: 'none'};
+  chrome.app.window.create('internal/main.html', windowOptions, (newWindow) => {
+    newWindow.maximize();
+  });
 });
diff --git a/chrome/browser/resources/downloads/item.js b/chrome/browser/resources/downloads/item.js
index 19ffb11..5eb28c7 100644
--- a/chrome/browser/resources/downloads/item.js
+++ b/chrome/browser/resources/downloads/item.js
@@ -244,6 +244,7 @@
      */
     computeIcon_: function() {
       if (loadTimeData.getBoolean('requestsApVerdicts') &&
+          this.data &&
           this.data.dangerType == downloads.DangerType.UNCOMMON_CONTENT) {
         return 'cr:error';
       }
diff --git a/chrome/browser/resources/local_ntp/customize.css b/chrome/browser/resources/local_ntp/customize.css
index c719258..e673900 100644
--- a/chrome/browser/resources/local_ntp/customize.css
+++ b/chrome/browser/resources/local_ntp/customize.css
@@ -21,6 +21,7 @@
   align-items: center;
   border-radius: 500px;
   bottom: 16px;
+  cursor: pointer;
   display: flex;
   height: 32px;
   justify-content: center;
@@ -33,7 +34,6 @@
 #edit-bg.ep-enhanced {
   background-color: rgb(255, 255, 255);
   box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 1px 2px rgba(0, 0, 0, 0.23);
-  cursor: pointer;
 }
 
 #edit-bg:hover,
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index d8df1ab..2d92b167 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -924,10 +924,17 @@
       }
 
       // Should be fetching the Google Drive destination.
+      return this.startLoadGoogleDrive();
+    }
+
+    /**
+     * Tries to load the Google Drive destination for the active user.
+     * @return {boolean}
+     */
+    startLoadGoogleDrive() {
       const driveKey = print_preview.createDestinationKey(
           print_preview.Destination.GooglePromotedId.DOCS,
           print_preview.DestinationOrigin.COOKIES, this.activeUser_);
-      assert(key === driveKey);
       return this.fetchPreselectedDestination_({
         id: print_preview.Destination.GooglePromotedId.DOCS,
         origin: print_preview.DestinationOrigin.COOKIES,
diff --git a/chrome/browser/resources/print_preview/data/user_manager.js b/chrome/browser/resources/print_preview/data/user_manager.js
index 56c84c9..ac2814d 100644
--- a/chrome/browser/resources/print_preview/data/user_manager.js
+++ b/chrome/browser/resources/print_preview/data/user_manager.js
@@ -62,8 +62,11 @@
     this.initialized_ = false;
   },
 
-  /** @param {?Array<string>} userAccounts */
-  initUserAccounts: function(userAccounts) {
+  /**
+   * @param {?Array<string>} userAccounts
+   * @param {boolean} syncAvailable
+   */
+  initUserAccounts: function(userAccounts, syncAvailable) {
     assert(!this.initialized_);
     this.initialized_ = true;
 
@@ -74,9 +77,20 @@
 
     // If cloud print is enabled, listen for account changes.
     assert(!this.cloudPrintDisabled);
-    this.addWebUIListener(
-        'user-accounts-updated', this.updateUsers_.bind(this));
-    this.updateUsers_(userAccounts);
+    if (syncAvailable) {
+      this.addWebUIListener(
+          'user-accounts-updated', this.updateUsers_.bind(this));
+      this.updateUsers_(userAccounts);
+    } else {
+      // Request the Google Docs destination from the Google Cloud Print server
+      // directly. We have to do this in incognito mode in order to get the
+      // user's login state.
+      this.destinationStore.startLoadGoogleDrive();
+      this.addWebUIListener('check-for-account-update', () => {
+        this.destinationStore.startLoadCloudDestinations(
+            print_preview.DestinationOrigin.COOKIES);
+      });
+    }
   },
 
   /** @param {!cloudprint.CloudPrintInterface} cloudPrintInterface */
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index da69df1..7b3f097 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -44,7 +44,8 @@
  *   serializedAppStateStr: ?string,
  *   serializedDefaultDestinationSelectionRulesStr: ?string,
  *   cloudPrintURL: (string | undefined),
- *   userAccounts: (Array<string> | undefined)
+ *   userAccounts: (Array<string> | undefined),
+ *   syncAvailable: boolean
  * }}
  * @see corresponding field name definitions in print_preview_handler.cc
  */
diff --git a/chrome/browser/resources/print_preview/polymer3/demo.js b/chrome/browser/resources/print_preview/polymer3/demo.js
index 2e1415c..6a59ffd 100644
--- a/chrome/browser/resources/print_preview/polymer3/demo.js
+++ b/chrome/browser/resources/print_preview/polymer3/demo.js
@@ -4,7 +4,7 @@
 
 import 'chrome://resources/polymer/v3_0/paper-button/paper-button.js';
 
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer-element.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 class HelloPolymer3Element extends PolymerElement {
   static get template() {
diff --git a/chrome/browser/resources/print_preview/ui/app.js b/chrome/browser/resources/print_preview/ui/app.js
index 7c15940..3b6c9e67 100644
--- a/chrome/browser/resources/print_preview/ui/app.js
+++ b/chrome/browser/resources/print_preview/ui/app.js
@@ -277,7 +277,7 @@
       this.$.sidebar.init(
           settings.isInAppKioskMode, settings.printerName,
           settings.serializedDefaultDestinationSelectionRulesStr,
-          settings.userAccounts || null);
+          settings.userAccounts || null, settings.syncAvailable);
       this.isInKioskAutoPrintMode_ = settings.isInKioskAutoPrintMode;
 
       // This is only visible in the task manager.
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.js b/chrome/browser/resources/print_preview/ui/destination_settings.js
index f8ac368..0701852 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.js
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.js
@@ -177,10 +177,14 @@
    * @param {string} serializedDefaultDestinationRulesStr String with rules for
    *     selecting a default destination.
    * @param {?Array<string>} userAccounts The signed in user accounts.
+   * @param {boolean} syncAvailable Whether sync is available. Used to determine
+   *     whether to wait for user info updates from the handler, or to always
+   *     send requests to the Google Cloud Print server.
    */
   init: function(
-      defaultPrinter, serializedDefaultDestinationRulesStr, userAccounts) {
-    this.$.userManager.initUserAccounts(userAccounts);
+      defaultPrinter, serializedDefaultDestinationRulesStr, userAccounts,
+      syncAvailable) {
+    this.$.userManager.initUserAccounts(userAccounts, syncAvailable);
     this.destinationStore_.init(
         this.appKioskMode, defaultPrinter, serializedDefaultDestinationRulesStr,
         /** @type {!Array<print_preview.RecentDestination>} */
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.js b/chrome/browser/resources/print_preview/ui/sidebar.js
index 8ac63482..90f5915 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.js
+++ b/chrome/browser/resources/print_preview/ui/sidebar.js
@@ -111,13 +111,15 @@
    * @param {string} serializedDestinationSelectionRulesStr String with rules
    *     for selecting the default destination.
    * @param {?Array<string>} userAccounts The signed in user accounts.
+   * @param {boolean} syncAvailable
    */
   init: function(
       appKioskMode, defaultPrinter, serializedDestinationSelectionRulesStr,
-      userAccounts) {
+      userAccounts, syncAvailable) {
     this.isInAppKioskMode_ = appKioskMode;
     this.$.destinationSettings.init(
-        defaultPrinter, serializedDestinationSelectionRulesStr, userAccounts);
+        defaultPrinter, serializedDestinationSelectionRulesStr, userAccounts,
+        syncAvailable);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html b/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html
index b4caef1..409a9582 100644
--- a/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html
+++ b/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html
@@ -22,7 +22,7 @@
           $i18n{assignSelectSwitchLabel}
         </div>
         <settings-dropdown-menu label="$i18n{assignSelectSwitchLabel}"
-            pref="{{prefs.switch_access.select.dropdown}}"
+            pref="{{prefs.switch_access.select.setting}}"
             menu-options="[[switchAssignOptions_]]"
             on-settings-control-change="onSelectAssigned_">
         </settings-dropdown-menu>
@@ -32,7 +32,7 @@
           $i18n{assignNextSwitchLabel}
         </div>
         <settings-dropdown-menu label="$i18n{assignNextSwitchLabel}"
-            pref="{{prefs.switch_access.next.dropdown}}"
+            pref="{{prefs.switch_access.next.setting}}"
             menu-options="[[switchAssignOptions_]]"
             on-settings-control-change="onNextAssigned_">
         </settings-dropdown-menu>
@@ -42,7 +42,7 @@
           $i18n{assignPreviousSwitchLabel}
         </div>
         <settings-dropdown-menu label="$i18n{assignPreviousSwitchLabel}"
-            pref="{{prefs.switch_access.previous.dropdown}}"
+            pref="{{prefs.switch_access.previous.setting}}"
             menu-options="[[switchAssignOptions_]]"
             on-settings-control-change="onPreviousAssigned_">
         </settings-dropdown-menu>
diff --git a/chrome/browser/search/iframe_source.cc b/chrome/browser/search/iframe_source.cc
index 16669cc9..4eab320c 100644
--- a/chrome/browser/search/iframe_source.cc
+++ b/chrome/browser/search/iframe_source.cc
@@ -18,8 +18,7 @@
 
 IframeSource::~IframeSource() = default;
 
-std::string IframeSource::GetMimeType(
-    const std::string& path_and_query) const {
+std::string IframeSource::GetMimeType(const std::string& path_and_query) {
   std::string path(GURL("chrome-search://host/" + path_and_query).path());
   if (base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII))
     return "application/javascript";
@@ -34,21 +33,21 @@
   return std::string();
 }
 
-bool IframeSource::AllowCaching() const {
+bool IframeSource::AllowCaching() {
   return false;
 }
 
 bool IframeSource::ShouldServiceRequest(
     const GURL& url,
     content::ResourceContext* resource_context,
-    int render_process_id) const {
+    int render_process_id) {
   return InstantIOContext::ShouldServiceRequest(url, resource_context,
                                                 render_process_id) &&
          url.SchemeIs(chrome::kChromeSearchScheme) &&
          url.host_piece() == GetSource() && ServesPath(url.path());
 }
 
-bool IframeSource::ShouldDenyXFrameOptions() const {
+bool IframeSource::ShouldDenyXFrameOptions() {
   return false;
 }
 
diff --git a/chrome/browser/search/iframe_source.h b/chrome/browser/search/iframe_source.h
index fba0e60..fca56596 100644
--- a/chrome/browser/search/iframe_source.h
+++ b/chrome/browser/search/iframe_source.h
@@ -24,12 +24,12 @@
 
  protected:
   // Overridden from content::URLDataSource:
-  std::string GetMimeType(const std::string& path_and_query) const override;
-  bool AllowCaching() const override;
-  bool ShouldDenyXFrameOptions() const override;
+  std::string GetMimeType(const std::string& path_and_query) override;
+  bool AllowCaching() override;
+  bool ShouldDenyXFrameOptions() override;
   bool ShouldServiceRequest(const GURL& url,
                             content::ResourceContext* resource_context,
-                            int render_process_id) const override;
+                            int render_process_id) override;
 
   // Returns whether this source should serve data for a particular path.
   virtual bool ServesPath(const std::string& path) const = 0;
diff --git a/chrome/browser/search/iframe_source_unittest.cc b/chrome/browser/search/iframe_source_unittest.cc
index 64031f3..f3514505 100644
--- a/chrome/browser/search/iframe_source_unittest.cc
+++ b/chrome/browser/search/iframe_source_unittest.cc
@@ -40,7 +40,7 @@
   void set_origin(std::string origin) { origin_ = origin; }
 
  protected:
-  std::string GetSource() const override { return "test"; }
+  std::string GetSource() override { return "test"; }
 
   bool ServesPath(const std::string& path) const override {
     return path == "/valid.html" || path == "/valid.js";
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 57bf875..dc577c0 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -817,7 +817,7 @@
 
 LocalNtpSource::~LocalNtpSource() = default;
 
-std::string LocalNtpSource::GetSource() const {
+std::string LocalNtpSource::GetSource() {
   return chrome::kChromeSearchLocalNtpHost;
 }
 
@@ -1053,8 +1053,7 @@
   callback.Run(nullptr);
 }
 
-std::string LocalNtpSource::GetMimeType(
-    const std::string& path) const {
+std::string LocalNtpSource::GetMimeType(const std::string& path) {
   const std::string stripped_path = StripParameters(path);
   for (size_t i = 0; i < base::size(kResources); ++i) {
     if (stripped_path == kResources[i].filename)
@@ -1063,7 +1062,7 @@
   return std::string();
 }
 
-bool LocalNtpSource::AllowCaching() const {
+bool LocalNtpSource::AllowCaching() {
   // Some resources served by LocalNtpSource, i.e. config.js, are dynamically
   // generated and could differ on each access. To avoid using old cached
   // content on reload, disallow caching here. Otherwise, it fails to reflect
@@ -1074,20 +1073,20 @@
 bool LocalNtpSource::ShouldServiceRequest(
     const GURL& url,
     content::ResourceContext* resource_context,
-    int render_process_id) const {
+    int render_process_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   return ShouldServiceRequestIOThread(url, resource_context, render_process_id);
 }
 
-bool LocalNtpSource::ShouldAddContentSecurityPolicy() const {
+bool LocalNtpSource::ShouldAddContentSecurityPolicy() {
   // The Content Security Policy is served as a meta tag in local NTP html.
   // We disable the HTTP Header version here to avoid a conflicting policy. See
   // GetContentSecurityPolicy.
   return false;
 }
 
-std::string LocalNtpSource::GetContentSecurityPolicy() const {
+std::string LocalNtpSource::GetContentSecurityPolicy() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 #if !defined(GOOGLE_CHROME_BUILD)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
diff --git a/chrome/browser/search/local_ntp_source.h b/chrome/browser/search/local_ntp_source.h
index a94d670..5171b3002 100644
--- a/chrome/browser/search/local_ntp_source.h
+++ b/chrome/browser/search/local_ntp_source.h
@@ -70,20 +70,20 @@
   };
 
   // Overridden from content::URLDataSource:
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string& path) const override;
-  bool AllowCaching() const override;
+  std::string GetMimeType(const std::string& path) override;
+  bool AllowCaching() override;
   bool ShouldServiceRequest(const GURL& url,
                             content::ResourceContext* resource_context,
-                            int render_process_id) const override;
-  bool ShouldAddContentSecurityPolicy() const override;
+                            int render_process_id) override;
+  bool ShouldAddContentSecurityPolicy() override;
 
   // The Content Security Policy for the Local NTP.
-  std::string GetContentSecurityPolicy() const;
+  std::string GetContentSecurityPolicy();
 
   // Overridden from NtpBackgroundServiceObserver:
   void OnCollectionInfoAvailable() override;
diff --git a/chrome/browser/search/most_visited_iframe_source.cc b/chrome/browser/search/most_visited_iframe_source.cc
index b91a352..5aa8a9c 100644
--- a/chrome/browser/search/most_visited_iframe_source.cc
+++ b/chrome/browser/search/most_visited_iframe_source.cc
@@ -50,7 +50,7 @@
 
 MostVisitedIframeSource::~MostVisitedIframeSource() = default;
 
-std::string MostVisitedIframeSource::GetSource() const {
+std::string MostVisitedIframeSource::GetSource() {
   return chrome::kChromeSearchMostVisitedHost;
 }
 
diff --git a/chrome/browser/search/most_visited_iframe_source.h b/chrome/browser/search/most_visited_iframe_source.h
index f4896f5..b5f3f3e 100644
--- a/chrome/browser/search/most_visited_iframe_source.h
+++ b/chrome/browser/search/most_visited_iframe_source.h
@@ -28,7 +28,7 @@
 
  private:
   // Overridden from IframeSource:
-  std::string GetSource() const override;
+  std::string GetSource() override;
 
   bool ServesPath(const std::string& path) const override;
 
diff --git a/chrome/browser/search/ntp_icon_source.cc b/chrome/browser/search/ntp_icon_source.cc
index 24a4792..c6275e6 100644
--- a/chrome/browser/search/ntp_icon_source.cc
+++ b/chrome/browser/search/ntp_icon_source.cc
@@ -275,7 +275,7 @@
 
 NtpIconSource::~NtpIconSource() = default;
 
-std::string NtpIconSource::GetSource() const {
+std::string NtpIconSource::GetSource() {
   return chrome::kChromeUINewTabIconHost;
 }
 
@@ -337,7 +337,7 @@
   }
 }
 
-std::string NtpIconSource::GetMimeType(const std::string&) const {
+std::string NtpIconSource::GetMimeType(const std::string&) {
   // NOTE: this may not always be correct for all possible types that this
   // source will serve. Seems to work fine, however.
   return "image/png";
@@ -346,7 +346,7 @@
 bool NtpIconSource::ShouldServiceRequest(
     const GURL& url,
     content::ResourceContext* resource_context,
-    int render_process_id) const {
+    int render_process_id) {
   if (url.SchemeIs(chrome::kChromeSearchScheme)) {
     return InstantIOContext::ShouldServiceRequest(url, resource_context,
                                                   render_process_id);
diff --git a/chrome/browser/search/ntp_icon_source.h b/chrome/browser/search/ntp_icon_source.h
index df56900..4c4a038 100644
--- a/chrome/browser/search/ntp_icon_source.h
+++ b/chrome/browser/search/ntp_icon_source.h
@@ -35,15 +35,15 @@
   ~NtpIconSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetMimeType(const std::string& path) override;
   bool ShouldServiceRequest(const GURL& url,
                             content::ResourceContext* resource_context,
-                            int render_process_id) const override;
+                            int render_process_id) override;
 
  private:
   struct NtpIconRequest;
diff --git a/chrome/browser/search/suggestions/suggestions_ui.cc b/chrome/browser/search/suggestions/suggestions_ui.cc
index 7677a90..39d39f1 100644
--- a/chrome/browser/search/suggestions/suggestions_ui.cc
+++ b/chrome/browser/search/suggestions/suggestions_ui.cc
@@ -25,12 +25,12 @@
   ~SuggestionsSourceWrapper() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetMimeType(const std::string& path) override;
 
  private:
   SuggestionsSource suggestions_source_;
@@ -45,7 +45,7 @@
 
 SuggestionsSourceWrapper::~SuggestionsSourceWrapper() {}
 
-std::string SuggestionsSourceWrapper::GetSource() const {
+std::string SuggestionsSourceWrapper::GetSource() {
   return chrome::kChromeUISuggestionsHost;
 }
 
@@ -56,8 +56,7 @@
   suggestions_source_.StartDataRequest(path, callback);
 }
 
-std::string SuggestionsSourceWrapper::GetMimeType(
-    const std::string& path) const {
+std::string SuggestionsSourceWrapper::GetMimeType(const std::string& path) {
   return "text/html";
 }
 
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
index fd79e52..09744085 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
@@ -401,7 +401,7 @@
 bool ChromeSSLHostStateDelegate::DidHostRunInsecureContent(
     const std::string& host,
     int child_id,
-    InsecureContentType content_type) const {
+    InsecureContentType content_type) {
   auto entry = BrokenHostEntry(host, child_id);
   switch (content_type) {
     case MIXED_CONTENT:
@@ -424,8 +424,7 @@
                                      std::string(), nullptr);
 }
 
-bool ChromeSSLHostStateDelegate::HasAllowException(
-    const std::string& host) const {
+bool ChromeSSLHostStateDelegate::HasAllowException(const std::string& host) {
   GURL url = GetSecureGURLForHost(host);
   const ContentSettingsPattern pattern =
       ContentSettingsPattern::FromURLNoWildcard(url);
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.h b/chrome/browser/ssl/chrome_ssl_host_state_delegate.h
index 77b8a9f1..870949b8 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.h
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.h
@@ -53,12 +53,11 @@
   void HostRanInsecureContent(const std::string& host,
                               int child_id,
                               InsecureContentType content_type) override;
-  bool DidHostRunInsecureContent(
-      const std::string& host,
-      int child_id,
-      InsecureContentType content_type) const override;
+  bool DidHostRunInsecureContent(const std::string& host,
+                                 int child_id,
+                                 InsecureContentType content_type) override;
   void RevokeUserAllowExceptions(const std::string& host) override;
-  bool HasAllowException(const std::string& host) const override;
+  bool HasAllowException(const std::string& host) override;
 
   // RevokeUserAllowExceptionsHard is the same as RevokeUserAllowExceptions but
   // additionally may close idle connections in the process. This should be used
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 1f55ee26a..a9ee1f3 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3900,6 +3900,8 @@
 
   if (is_chromeos) {
     sources += [
+      "ash/ash_test_util.cc",
+      "ash/ash_test_util.h",
       "ash/fake_tablet_mode_controller.cc",
       "ash/fake_tablet_mode_controller.h",
       "ash/test_login_screen.cc",
diff --git a/chrome/browser/ui/ash/OWNERS b/chrome/browser/ui/ash/OWNERS
index 68771a87..54238d96 100644
--- a/chrome/browser/ui/ash/OWNERS
+++ b/chrome/browser/ui/ash/OWNERS
@@ -1,4 +1,3 @@
-derat@chromium.org
 jamescook@chromium.org
 oshima@chromium.org
 sky@chromium.org
diff --git a/chrome/browser/ui/ash/ash_test_util.cc b/chrome/browser/ui/ash/ash_test_util.cc
new file mode 100644
index 0000000..2be6d2b
--- /dev/null
+++ b/chrome/browser/ui/ash/ash_test_util.cc
@@ -0,0 +1,78 @@
+// Copyright 2019 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/ui/ash/ash_test_util.h"
+
+#include "ash/public/cpp/window_properties.h"
+#include "ash/public/cpp/window_state_type.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
+#include "ui/base/test/ui_controls.h"
+#include "ui/wm/core/window_util.h"
+
+namespace test {
+namespace {
+// Wait until the window's state changes to given the snapped state.
+// The window should stay alive, so no need to observer destroying.
+class SnapWaiter : public aura::WindowObserver {
+ public:
+  SnapWaiter(aura::Window* window, ash::WindowStateType type)
+      : window_(window), type_(type) {
+    window->AddObserver(this);
+  }
+  ~SnapWaiter() override { window_->RemoveObserver(this); }
+
+  // aura::WindowObserver:
+  void OnWindowPropertyChanged(aura::Window* window,
+                               const void* key,
+                               intptr_t old) override {
+    if (key == ash::kWindowStateTypeKey && IsSnapped())
+      run_loop_.Quit();
+  }
+
+  void Wait() { run_loop_.Run(); }
+
+  bool IsSnapped() const {
+    return window_->GetProperty(ash::kWindowStateTypeKey) == type_;
+  }
+
+ private:
+  aura::Window* window_;
+  ash::WindowStateType type_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(SnapWaiter);
+};
+
+}  // namespace
+
+void ActivateAndSnapWindow(aura::Window* window, ash::WindowStateType type) {
+  DCHECK(window);
+  if (!wm::IsActiveWindow(window))
+    wm::ActivateWindow(window);
+
+  ASSERT_TRUE(wm::IsActiveWindow(window));
+
+  SnapWaiter snap_waiter(window, type);
+  ASSERT_TRUE(type == ash::WindowStateType::kRightSnapped ||
+              type == ash::WindowStateType::kLeftSnapped);
+
+  // Early return if it's already snapped.
+  if (snap_waiter.IsSnapped())
+    return;
+
+  ui_controls::SendKeyPress(window,
+                            type == ash::WindowStateType::kLeftSnapped
+                                ? ui::VKEY_OEM_4
+                                : ui::VKEY_OEM_6,
+                            /*control=*/false,
+                            /*shift=*/false,
+                            /*alt=*/true,
+                            /*command=*/false);
+  snap_waiter.Wait();
+}
+
+}  // namespace test
diff --git a/chrome/browser/ui/ash/ash_test_util.h b/chrome/browser/ui/ash/ash_test_util.h
new file mode 100644
index 0000000..7fd082b6
--- /dev/null
+++ b/chrome/browser/ui/ash/ash_test_util.h
@@ -0,0 +1,23 @@
+// Copyright 2019 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_UI_ASH_ASH_TEST_UTIL_H_
+#define CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+enum class WindowStateType;
+}
+
+namespace test {
+
+// The snap window. This will activate the |window|.
+void ActivateAndSnapWindow(aura::Window* window, ash::WindowStateType type);
+
+}  // namespace test
+
+#endif  // CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
diff --git a/chrome/browser/ui/ash/split_view_interactive_uitest.cc b/chrome/browser/ui/ash/split_view_interactive_uitest.cc
index f52e9c40..1f42475 100644
--- a/chrome/browser/ui/ash/split_view_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/split_view_interactive_uitest.cc
@@ -2,157 +2,164 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/test/shell_test_api.h"
-#include "ash/shell.h"
-#include "ash/wm/splitview/split_view_controller.h"
+#include "ash/public/cpp/window_state_type.h"
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
+#include "chrome/browser/ui/ash/ash_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/perf/drag_event_generator.h"
 #include "chrome/test/base/perf/performance_test.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_tree_host.h"
 #include "ui/compositor/compositor.h"
-#include "ui/compositor/compositor_switches.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
-#include "ui/gl/gl_switches.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_observer.h"
+#include "ui/wm/core/wm_core_switches.h"
 
 namespace {
 
-class SplitViewTest : public UIPerformanceTest {
+class SplitViewTest
+    : public UIPerformanceTest,
+      public testing::WithParamInterface<::testing::tuple<bool, bool>> {
  public:
   SplitViewTest() = default;
   ~SplitViewTest() override = default;
 
   // UIPerformanceTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(ash::switches::kAshEnableTabletMode);
+    // We're not interested in window transition animation in this test,
+    // so just disable it.
+    command_line->AppendSwitch(wm::switches::kWindowAnimationsDisabled);
+  }
+  void SetUpOnMainThread() override {
+    UIPerformanceTest::SetUpOnMainThread();
+
+    use_ntp_ = std::get<0>(GetParam());
+    use_touch_ = std::get<1>(GetParam());
+
+    if (use_ntp_)
+      ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+  }
+
+  bool use_touch() const { return use_touch_; }
+
+  void SetUMAToObserve(std::string name) { uma_name_ = std::move(name); }
+
+  Browser* CreateBrowserMaybeWithNtp() {
+    Browser* new_browser = CreateBrowser(browser()->profile());
+    if (use_ntp_)
+      ui_test_utils::NavigateToURL(new_browser,
+                                   GURL(chrome::kChromeUINewTabURL));
+    return new_browser;
+  }
+
+  void WaitForWarmup() {
+    // If running on device, wait a bit so that the display is actually powered
+    // on.
+    // TODO: this should be better factored.
+    base::TimeDelta warmup = base::TimeDelta::FromSeconds(
+        base::SysInfo::IsRunningOnChromeOS() ? 5 : 0);
+    // Give ntp at least 1 seconds to be fully resized.
+    if (use_ntp_ && warmup.is_zero())
+      warmup += base::TimeDelta::FromSeconds(1);
+
+    if (!warmup.is_zero()) {
+      base::RunLoop run_loop;
+      base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), warmup);
+      run_loop.Run();
+    }
   }
 
  private:
   std::vector<std::string> GetUMAHistogramNames() const override {
-    return {"Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow"};
+    return {uma_name_};
   }
 
+  bool use_ntp_;
+  bool use_touch_;
+  std::string uma_name_;
+
   DISALLOW_COPY_AND_ASSIGN(SplitViewTest);
 };
 
-// Used to wait for a window resize to show up on screen.
-class WidgetResizeWaiter : public views::WidgetObserver {
- public:
-  explicit WidgetResizeWaiter(views::Widget* widget) : widget_(widget) {
-    widget_->AddObserver(this);
-  }
+IN_PROC_BROWSER_TEST_P(SplitViewTest, ResizeTwoWindows) {
+  SetUMAToObserve(
+      "Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow");
 
-  ~WidgetResizeWaiter() override {
-    widget_->RemoveObserver(this);
-    EXPECT_FALSE(waiting_for_frame_);
-  }
-
-  void WaitForDisplay() {
-    do {
-      if (waiting_for_frame_) {
-        run_loop_ = std::make_unique<base::RunLoop>();
-        run_loop_->Run();
-        EXPECT_FALSE(waiting_for_frame_);
-      }
-      ash::ShellTestApi().WaitForNoPointerHoldLock();
-    } while (waiting_for_frame_);
-  }
-
- private:
-  void OnFramePresented(const gfx::PresentationFeedback& feedback) {
-    waiting_for_frame_ = false;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
-  // views::WidgetObserver:
-  void OnWidgetBoundsChanged(views::Widget* widget,
-                             const gfx::Rect& new_bounds) override {
-    ui::Compositor* compositor =
-        widget->GetNativeWindow()->GetHost()->compositor();
-    ASSERT_TRUE(compositor);
-    waiting_for_frame_ = true;
-    compositor->RequestPresentationTimeForNextFrame(base::BindOnce(
-        &WidgetResizeWaiter::OnFramePresented, base::Unretained(this)));
-  }
-
-  views::Widget* widget_;
-  bool waiting_for_frame_ = false;
-  std::unique_ptr<base::RunLoop> run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(WidgetResizeWaiter);
-};
-
-IN_PROC_BROWSER_TEST_F(SplitViewTest, SplitViewResize) {
   // This test is intended to gauge performance of resizing windows while in
   // tablet mode split view. It does the following:
   // . creates two browser windows.
-  // . enters tablet mode.
   // . snaps one to the left, one to the right.
+  // . enters tablet mode.
   // . drags the resizer, which triggers resizing both browsers.
-  browser()->window()->Maximize();
 
-  Browser* browser2 = CreateBrowser(browser()->profile());
-  browser2->window()->Show();
-  browser2->window()->Maximize();
+  aura::Window* browser_window = browser()->window()->GetNativeWindow();
+  test::ActivateAndSnapWindow(browser_window,
+                              ash::WindowStateType::kLeftSnapped);
 
-  // If running on device, wait a bit so that the display is actually powered
-  // on.
-  // TODO: this should be better factored.
-  if (base::SysInfo::IsRunningOnChromeOS()) {
-    base::RunLoop run_loop;
-    base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
-                          base::TimeDelta::FromSeconds(5));
-    run_loop.Run();
-  }
-
+  Browser* browser2 = CreateBrowserMaybeWithNtp();
+  aura::Window* browser2_window = browser2->window()->GetNativeWindow();
+  test::ActivateAndSnapWindow(browser2_window,
+                              ash::WindowStateType::kRightSnapped);
   ash::ShellTestApi().EnableTabletModeWindowManager(true);
 
-  views::Widget* browser_widget =
-      BrowserView::GetBrowserViewForBrowser(browser())->GetWidget();
-  views::Widget* browser2_widget =
-      BrowserView::GetBrowserViewForBrowser(browser2)->GetWidget();
-  ash::Shell* shell = ash::Shell::Get();
-  shell->split_view_controller()->SnapWindow(browser2_widget->GetNativeWindow(),
-                                             ash::SplitViewController::LEFT);
-  shell->split_view_controller()->SnapWindow(browser_widget->GetNativeWindow(),
-                                             ash::SplitViewController::RIGHT);
-
-  ash::ShellTestApi().WaitForNoPointerHoldLock();
+  WaitForWarmup();
 
   const gfx::Size display_size =
       display::Screen::GetScreen()->GetPrimaryDisplay().bounds().size();
-  const gfx::Point start_position(display_size.width() / 2,
-                                  display_size.height() / 2);
+  const gfx::Point start_position(gfx::Rect(display_size).CenterPoint());
   TRACE_EVENT_ASYNC_BEGIN0("ui", "Interaction.ui_WindowResize", this);
-  ASSERT_TRUE(
-      ui_test_utils::SendMouseMoveSync(
-          gfx::Point(start_position.x(), start_position.y())) &&
-      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
-
-  constexpr int kNumIncrements = 20;
-  int width = browser_widget->GetWindowBoundsInScreen().width();
-  for (int i = 0; i < kNumIncrements; ++i) {
-    const gfx::Point resize_point(start_position.x() - i - 1,
-                                  start_position.y());
-    WidgetResizeWaiter observer(browser_widget);
-    ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(resize_point));
-    observer.WaitForDisplay();
-    ++width;
-    EXPECT_EQ(width, browser_widget->GetWindowBoundsInScreen().width());
-  }
+  gfx::Point end_position(start_position);
+  end_position.set_x(end_position.x() - 60);
+  ui_test_utils::DragEventGenerator generator(
+      std::make_unique<ui_test_utils::InterpolatedProducer>(
+          start_position, end_position,
+          base::TimeDelta::FromMilliseconds(1000)),
+      use_touch());
+  generator.Wait();
   TRACE_EVENT_ASYNC_END0("ui", "Interaction.ui_WindowResize", this);
 }
 
+IN_PROC_BROWSER_TEST_P(SplitViewTest, ResizeWithOverview) {
+  SetUMAToObserve(
+      "Ash.SplitViewResize.PresentationTime.TabletMode.WithOverview");
+
+  Browser* browser2 = CreateBrowserMaybeWithNtp();
+
+  aura::Window* browser2_window = browser2->window()->GetNativeWindow();
+  test::ActivateAndSnapWindow(browser2_window,
+                              ash::WindowStateType::kRightSnapped);
+
+  ash::ShellTestApi().EnableTabletModeWindowManager(true);
+  ash::ShellTestApi().WaitForOverviewAnimationState(
+      ash::OverviewAnimationState::kEnterAnimationComplete);
+
+  WaitForWarmup();
+
+  const gfx::Point start_position =
+      display::Screen::GetScreen()->GetPrimaryDisplay().bounds().CenterPoint();
+  TRACE_EVENT_ASYNC_BEGIN0("ui", "Interaction.ui_WindowResize", this);
+  gfx::Point end_position(start_position);
+  end_position.set_x(end_position.x() - 60);
+  ui_test_utils::DragEventGenerator generator(
+      std::make_unique<ui_test_utils::InterpolatedProducer>(
+          start_position, end_position,
+          base::TimeDelta::FromMilliseconds(1000)),
+      use_touch());
+  generator.Wait();
+  TRACE_EVENT_ASYNC_END0("ui", "Interaction.ui_WindowResize", this);
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         SplitViewTest,
+                         ::testing::Combine(/*ntp=*/testing::Bool(),
+                                            /*touch=*/testing::Bool()));
+
 }  // namespace
diff --git a/chrome/browser/ui/ash/window_resize_interactive_uitest.cc b/chrome/browser/ui/ash/window_resize_interactive_uitest.cc
index 46cebb5..9696aff 100644
--- a/chrome/browser/ui/ash/window_resize_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/window_resize_interactive_uitest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/window_properties.h"
 #include "ash/public/cpp/window_state_type.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
+#include "chrome/browser/ui/ash/ash_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/test/base/interactive_test_utils.h"
@@ -107,41 +107,6 @@
   DISALLOW_COPY_AND_ASSIGN(MultiPointProducer);
 };
 
-// Wait until the window's state changed to given snapped state.
-// The window should stay alive, so no need to observer destroying.
-class SnapWaiter : public aura::WindowObserver {
- public:
-  SnapWaiter(aura::Window* window, ash::WindowStateType type)
-      : window_(window), type_(type) {
-    window->AddObserver(this);
-  }
-  ~SnapWaiter() override { window_->RemoveObserver(this); }
-
-  // aura::WindowObserver:
-  void OnWindowPropertyChanged(aura::Window* window,
-                               const void* key,
-                               intptr_t old) override {
-    if (key == ash::kWindowStateTypeKey && IsSnapped())
-      run_loop_.Quit();
-  }
-
-  void Wait() {
-    if (!IsSnapped())
-      run_loop_.Run();
-  }
-
- private:
-  bool IsSnapped() const {
-    return window_->GetProperty(ash::kWindowStateTypeKey) == type_;
-  }
-
-  aura::Window* window_;
-  ash::WindowStateType type_;
-  base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(SnapWaiter);
-};
-
 }  // namespace
 
 // Test window resize performance in clamshell mode.
@@ -191,13 +156,8 @@
 
 IN_PROC_BROWSER_TEST_P(WindowResizeTest, Single) {
   aura::Window* browser_window = browser()->window()->GetNativeWindow();
-  SnapWaiter snap_waiter(browser_window, ash::WindowStateType::kLeftSnapped);
-  ui_controls::SendKeyPress(browser_window, ui::VKEY_OEM_4,
-                            /*control=*/false,
-                            /*shift=*/false,
-                            /*alt=*/true,
-                            /*command=*/false);
-  snap_waiter.Wait();
+  test::ActivateAndSnapWindow(browser_window,
+                              ash::WindowStateType::kLeftSnapped);
 
   gfx::Rect bounds = browser_window->GetBoundsInScreen();
   gfx::Point start_point = gfx::Point(bounds.right_center());
@@ -221,15 +181,8 @@
 
 IN_PROC_BROWSER_TEST_P(WindowResizeTest, Multi) {
   aura::Window* browser_window = browser()->window()->GetNativeWindow();
-  {
-    SnapWaiter snap_waiter(browser_window, ash::WindowStateType::kLeftSnapped);
-    ui_controls::SendKeyPress(browser_window, ui::VKEY_OEM_4,
-                              /*control=*/false,
-                              /*shift=*/false,
-                              /*alt=*/true,
-                              /*command=*/false);
-    snap_waiter.Wait();
-  }
+  test::ActivateAndSnapWindow(browser_window,
+                              ash::WindowStateType::kLeftSnapped);
 
   Browser* browser2 = CreateBrowser(browser()->profile());
   if (use_ntp()) {
@@ -237,18 +190,10 @@
     ui_test_utils::NavigateToURL(browser2, ntp_url);
   }
 
+  aura::Window* browser_window2 = browser2->window()->GetNativeWindow();
   // Snap Right
-  {
-    aura::Window* browser_window2 = browser2->window()->GetNativeWindow();
-    SnapWaiter snap_waiter(browser_window2,
-                           ash::WindowStateType::kRightSnapped);
-    ui_controls::SendKeyPress(browser_window2, ui::VKEY_OEM_6,
-                              /*control=*/false,
-                              /*shift=*/false,
-                              /*alt=*/true,
-                              /*command=*/false);
-    snap_waiter.Wait();
-  }
+  test::ActivateAndSnapWindow(browser_window2,
+                              ash::WindowStateType::kRightSnapped);
 
   gfx::Rect bounds = browser_window->GetBoundsInScreen();
   gfx::Point start_point = gfx::Point(bounds.right_center());
diff --git a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
index 70508a8..c386235 100644
--- a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
+++ b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
@@ -64,13 +64,18 @@
   // TODO(erikchen): Detect symbolic hot keys, and force control to be passed
   // back to AppKit so that it can handle it correctly.
   // https://crbug.com/846893.
+  auto* bridge =
+      remote_cocoa::NativeWidgetNSWindowBridge::GetFromNativeWindow(window);
 
   NSResponder* responder = [window firstResponder];
   if ([responder conformsToProtocol:@protocol(CommandDispatcherTarget)]) {
     NSObject<CommandDispatcherTarget>* target =
         static_cast<NSObject<CommandDispatcherTarget>*>(responder);
-    if ([target isKeyLocked:event])
+    if ([target isKeyLocked:event]) {
+      if (bridge)
+        bridge->SaveKeyEventForRedispatch(event);
       return ui::PerformKeyEquivalentResult::kUnhandled;
+    }
   }
 
   if ([self eventHandledByViewsFocusManager:event
@@ -94,8 +99,6 @@
   // highlighting of the NSMenu.
   CommandForKeyEventResult result = CommandForKeyEvent(event);
   if (result.found()) {
-    auto* bridge =
-        remote_cocoa::NativeWidgetNSWindowBridge::GetFromNativeWindow(window);
     if (bridge) {
       bool was_executed = false;
       bridge->host()->ExecuteCommand(
@@ -103,10 +106,11 @@
           true /* is_before_first_responder */, &was_executed);
       if (was_executed)
         return ui::PerformKeyEquivalentResult::kHandled;
-      bridge->SaveKeyEventForRedispatch(event);
     }
   }
 
+  if (bridge)
+    bridge->SaveKeyEventForRedispatch(event);
   return ui::PerformKeyEquivalentResult::kUnhandled;
 }
 
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
index d8547f5..c3376c9 100644
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
+++ b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
@@ -27,10 +27,10 @@
 }
 
 - (BOOL)isFullscreenTransitionInProgress {
-  views::NativeWidgetMacNSWindowHost* bridge_host =
+  auto* host =
       views::NativeWidgetMacNSWindowHost::GetFromNativeWindow([self window]);
-  if (bridge_host->bridge_impl())
-    return bridge_host->bridge_impl()->in_fullscreen_transition();
+  if (auto* bridge = host->GetInProcessNSWindowBridge())
+    return bridge->in_fullscreen_transition();
   DLOG(ERROR) << "TODO(https://crbug.com/915110): Support fullscreen "
                  "transitions for RemoteMacViews PWA windows.";
   return false;
@@ -39,11 +39,11 @@
 - (NSWindow*)window {
   NSWindow* ns_window = browserView_->GetNativeWindow().GetNativeNSWindow();
   if (!ns_view_) {
-    views::NativeWidgetMacNSWindowHost* bridge_host =
+    auto* host =
         views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(ns_window);
-    if (bridge_host) {
-      if (bridge_host->bridge_impl())
-        ns_view_.reset([bridge_host->bridge_impl()->ns_view() retain]);
+    if (host) {
+      if (auto* bridge = host->GetInProcessNSWindowBridge())
+        ns_view_.reset([bridge->ns_view() retain]);
       else
         DLOG(ERROR) << "Cannot retain remote NSView.";
     }
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc
index 753263a..cc0fec5 100644
--- a/chrome/browser/ui/managed_ui.cc
+++ b/chrome/browser/ui/managed_ui.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/managed_ui.h"
 
-#include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
@@ -12,7 +11,6 @@
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/management_ui_handler.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -27,9 +25,6 @@
 namespace chrome {
 
 bool ShouldDisplayManagedUi(Profile* profile) {
-  if (!base::FeatureList::IsEnabled(features::kShowManagedUi))
-    return false;
-
 #if defined(OS_CHROMEOS)
   // Don't show the UI in demo mode.
   if (chromeos::DemoSession::IsDeviceInDemoMode())
diff --git a/chrome/browser/ui/managed_ui_browsertest.cc b/chrome/browser/ui/managed_ui_browsertest.cc
index 4f14678f..2a30ddd 100644
--- a/chrome/browser/ui/managed_ui_browsertest.cc
+++ b/chrome/browser/ui/managed_ui_browsertest.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/managed_ui.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -37,30 +36,11 @@
   DISALLOW_COPY_AND_ASSIGN(ManagedUiTest);
 };
 
-IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiFlagDisabled) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitFromCommandLine("", "ShowManagedUi");
-
-  PolicyMap policy_map;
-  policy_map.Set("test-policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
-                 POLICY_SOURCE_PLATFORM,
-                 std::make_unique<base::Value>("hello world"), nullptr);
-  provider()->UpdateChromePolicy(policy_map);
-
-  EXPECT_FALSE(chrome::ShouldDisplayManagedUi(browser()->profile()));
-}
-
 IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiNoPolicies) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitFromCommandLine("ShowManagedUi", "");
-
   EXPECT_FALSE(chrome::ShouldDisplayManagedUi(browser()->profile()));
 }
 
 IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiOnDesktop) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitFromCommandLine("ShowManagedUi", "");
-
   PolicyMap policy_map;
   policy_map.Set("test-policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
                  POLICY_SOURCE_PLATFORM,
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
index 14fd4b8..c749627a 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
@@ -38,12 +38,12 @@
 
 SkColor AutofillPopupBaseView::GetSelectedBackgroundColor() {
   return GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_FocusedHighlightedMenuItemBackgroundColor);
+      ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor);
 }
 
 SkColor AutofillPopupBaseView::GetFooterBackgroundColor() {
   return GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_HighlightedMenuItemBackgroundColor);
+      ui::NativeTheme::kColorId_BubbleFooterBackground);
 }
 
 SkColor AutofillPopupBaseView::GetSeparatorColor() {
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index ce6c4233..e82ea18a 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -510,9 +510,9 @@
 
 std::unique_ptr<views::Background>
 AutofillPopupSuggestionView::CreateBackground() {
-  return views::CreateSolidBackground(
-      is_selected_ ? popup_view_->GetSelectedBackgroundColor()
-                   : popup_view_->GetBackgroundColor());
+  return is_selected_ ? views::CreateSolidBackground(
+                            popup_view_->GetSelectedBackgroundColor())
+                      : nullptr;
 }
 
 int AutofillPopupSuggestionView::GetPrimaryTextStyle() {
@@ -656,9 +656,9 @@
 }
 
 std::unique_ptr<views::Background> AutofillPopupFooterView::CreateBackground() {
-  return views::CreateSolidBackground(
-      is_selected_ ? popup_view_->GetSelectedBackgroundColor()
-                   : popup_view_->GetFooterBackgroundColor());
+  return is_selected_ ? views::CreateSolidBackground(
+                            popup_view_->GetSelectedBackgroundColor())
+                      : nullptr;
 }
 
 int AutofillPopupFooterView::GetPrimaryTextStyle() {
@@ -709,8 +709,6 @@
       /*bottom=*/0,
       /*right=*/0));
   AddChildView(separator);
-
-  SetBackground(CreateBackground());
 }
 
 void AutofillPopupSeparatorView::RefreshStyle() {
@@ -719,7 +717,7 @@
 
 std::unique_ptr<views::Background>
 AutofillPopupSeparatorView::CreateBackground() {
-  return views::CreateSolidBackground(popup_view_->GetBackgroundColor());
+  return nullptr;
 }
 
 AutofillPopupSeparatorView::AutofillPopupSeparatorView(
@@ -780,7 +778,7 @@
 
 std::unique_ptr<views::Background>
 AutofillPopupWarningView::CreateBackground() {
-  return views::CreateSolidBackground(popup_view_->GetBackgroundColor());
+  return nullptr;
 }
 
 }  // namespace
@@ -796,6 +794,10 @@
   RefreshStyle();
 }
 
+void AutofillPopupRowView::OnThemeChanged() {
+  RefreshStyle();
+}
+
 bool AutofillPopupRowView::OnMouseDragged(const ui::MouseEvent& event) {
   return true;
 }
@@ -833,6 +835,20 @@
 
 AutofillPopupViewNativeViews::~AutofillPopupViewNativeViews() {}
 
+void AutofillPopupViewNativeViews::OnThemeChanged() {
+  SetBackground(views::CreateSolidBackground(GetBackgroundColor()));
+  // |body_container_| and |footer_container_| will be null if there is no body
+  // or footer content, respectively.
+  if (body_container_) {
+    body_container_->SetBackground(
+        views::CreateSolidBackground(GetBackgroundColor()));
+  }
+  if (footer_container_) {
+    footer_container_->SetBackground(
+        views::CreateSolidBackground(GetFooterBackgroundColor()));
+  }
+}
+
 void AutofillPopupViewNativeViews::Show() {
   DoShow();
 }
@@ -911,7 +927,8 @@
 
   if (!rows_.empty()) {
     // Create a container to wrap the "regular" (non-footer) rows.
-    auto body_container = std::make_unique<views::View>();
+    std::unique_ptr<views::View> body_container =
+        std::make_unique<views::View>();
     views::BoxLayout* body_layout = body_container->SetLayoutManager(
         std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
     body_layout->set_main_axis_alignment(
@@ -922,11 +939,9 @@
 
     scroll_view_ = new views::ScrollView();
     scroll_view_->set_hide_horizontal_scrollbar(true);
-    auto* body_container_ptr =
-        scroll_view_->SetContents(std::move(body_container));
+    body_container_ = scroll_view_->SetContents(std::move(body_container));
     scroll_view_->set_draw_overflow_indicator(false);
-    scroll_view_->ClipHeightTo(0,
-                               body_container_ptr->GetPreferredSize().height());
+    scroll_view_->ClipHeightTo(0, body_container_->GetPreferredSize().height());
 
     // Use an additional container to apply padding outside the scroll view, so
     // that the padding area is stationary. This ensures that the rounded
@@ -947,9 +962,7 @@
   // affected by scrolling behavior (it's "sticky") and because it has a
   // special background color.
   if (has_footer) {
-    views::View* footer_container = new views::View();
-    footer_container->SetBackground(
-        views::CreateSolidBackground(GetFooterBackgroundColor()));
+    auto* footer_container = new views::View();
 
     views::BoxLayout* footer_layout = footer_container->SetLayoutManager(
         std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
@@ -964,8 +977,8 @@
       line_number++;
     }
 
-    AddChildView(footer_container);
-    layout_->SetFlexForView(footer_container, 0);
+    footer_container_ = AddChildView(footer_container);
+    layout_->SetFlexForView(footer_container_, 0);
   }
 }
 
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
index 0ce318b..54ec3645 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
@@ -33,6 +33,7 @@
   void SetSelected(bool is_selected);
 
   // views::View:
+  void OnThemeChanged() override;
   // Drags and presses on any row should be a no-op; subclasses instead rely on
   // entry/release events. Returns true to indicate that those events have been
   // processed (i.e., intentionally ignored).
@@ -70,6 +71,9 @@
     return rows_;
   }
 
+  // views::View:
+  void OnThemeChanged() override;
+
   // AutofillPopupView:
   void Show() override;
   void Hide() override;
@@ -97,10 +101,12 @@
   void DoUpdateBoundsAndRedrawPopup() override;
 
   // Controller for this view.
-  AutofillPopupController* controller_;
+  AutofillPopupController* controller_ = nullptr;
   std::vector<AutofillPopupRowView*> rows_;
-  views::BoxLayout* layout_;
-  views::ScrollView* scroll_view_;
+  views::BoxLayout* layout_ = nullptr;
+  views::ScrollView* scroll_view_ = nullptr;
+  views::View* body_container_ = nullptr;
+  views::View* footer_container_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(AutofillPopupViewNativeViews);
 };
diff --git a/chrome/browser/ui/views/frame/browser_frame_mac.mm b/chrome/browser/ui/views/frame/browser_frame_mac.mm
index 6542723..a9f6fb7 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_frame_mac.mm
@@ -332,14 +332,14 @@
 }
 
 void BrowserFrameMac::OnWindowInitialized() {
-  if (bridge_impl()) {
-    bridge_impl()->SetCommandDispatcher(
+  if (auto* bridge = GetInProcessNSWindowBridge()) {
+    bridge->SetCommandDispatcher(
         [[[ChromeCommandDispatcherDelegate alloc] init] autorelease],
         [[[BrowserWindowCommandHandler alloc] init] autorelease]);
   } else {
     if (auto* host = GetHostForBrowser(browser_view_->browser())) {
       host->GetAppShim()->CreateCommandDispatcherForWidget(
-          bridge_host()->bridged_native_widget_id());
+          GetNSWindowHost()->bridged_native_widget_id());
     }
   }
 }
@@ -409,5 +409,5 @@
 
   // Redispatch the event. If it's a keyEquivalent:, this gives
   // CommandDispatcher the opportunity to finish passing the event to consumers.
-  return bridge_host()->RedispatchKeyEvent(event.os_event);
+  return GetNSWindowHost()->RedispatchKeyEvent(event.os_event);
 }
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc
index f673c9c..d4cff18 100644
--- a/chrome/browser/ui/webui/about_ui.cc
+++ b/chrome/browser/ui/webui/about_ui.cc
@@ -580,7 +580,7 @@
 
 AboutUIHTMLSource::~AboutUIHTMLSource() {}
 
-std::string AboutUIHTMLSource::GetSource() const {
+std::string AboutUIHTMLSource::GetSource() {
   return source_name_;
 }
 
@@ -640,7 +640,7 @@
   callback.Run(base::RefCountedString::TakeString(&html_copy));
 }
 
-std::string AboutUIHTMLSource::GetMimeType(const std::string& path) const {
+std::string AboutUIHTMLSource::GetMimeType(const std::string& path) {
   if (path == kCreditsJsPath     ||
 #if defined(OS_CHROMEOS)
       path == kKeyboardUtilsPath ||
@@ -652,7 +652,7 @@
   return "text/html";
 }
 
-bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
+bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() {
 #if defined(OS_CHROMEOS)
   if (source_name_ == chrome::kChromeUIOSCreditsHost ||
       source_name_ == chrome::kChromeUILinuxCreditsHost) {
@@ -663,7 +663,7 @@
 }
 
 std::string AboutUIHTMLSource::GetAccessControlAllowOriginForOrigin(
-    const std::string& origin) const {
+    const std::string& origin) {
 #if defined(OS_CHROMEOS)
   // Allow chrome://oobe to load chrome://terms via XHR.
   if (source_name_ == chrome::kChromeUITermsHost &&
diff --git a/chrome/browser/ui/webui/about_ui.h b/chrome/browser/ui/webui/about_ui.h
index 801b98e..6860148 100644
--- a/chrome/browser/ui/webui/about_ui.h
+++ b/chrome/browser/ui/webui/about_ui.h
@@ -23,15 +23,15 @@
   ~AboutUIHTMLSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string& path) const override;
-  bool ShouldAddContentSecurityPolicy() const override;
+  std::string GetMimeType(const std::string& path) override;
+  bool ShouldAddContentSecurityPolicy() override;
   std::string GetAccessControlAllowOriginForOrigin(
-      const std::string& origin) const override;
+      const std::string& origin) override;
 
   // Send the response data.
   void FinishDataRequest(
diff --git a/chrome/browser/ui/webui/app_launcher_page_ui.cc b/chrome/browser/ui/webui/app_launcher_page_ui.cc
index 3bb63dbd..7005dd6 100644
--- a/chrome/browser/ui/webui/app_launcher_page_ui.cc
+++ b/chrome/browser/ui/webui/app_launcher_page_ui.cc
@@ -94,7 +94,7 @@
     : profile_(profile) {
 }
 
-std::string AppLauncherPageUI::HTMLSource::GetSource() const {
+std::string AppLauncherPageUI::HTMLSource::GetSource() {
   return chrome::kChromeUIAppLauncherPageHost;
 }
 
@@ -118,33 +118,30 @@
 }
 
 std::string AppLauncherPageUI::HTMLSource::GetMimeType(
-    const std::string& resource) const {
+    const std::string& resource) {
   return "text/html";
 }
 
-bool AppLauncherPageUI::HTMLSource::ShouldReplaceExistingSource() const {
+bool AppLauncherPageUI::HTMLSource::ShouldReplaceExistingSource() {
   return false;
 }
 
-bool AppLauncherPageUI::HTMLSource::AllowCaching() const {
+bool AppLauncherPageUI::HTMLSource::AllowCaching() {
   // Should not be cached to reflect dynamically-generated contents that may
   // depend on user profiles.
   return false;
 }
 
-std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyScriptSrc()
-    const {
+std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyScriptSrc() {
   // 'unsafe-inline' is added to script-src.
   return "script-src chrome://resources 'self' 'unsafe-eval' 'unsafe-inline';";
 }
 
-std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyStyleSrc()
-    const {
+std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyStyleSrc() {
   return "style-src 'self' chrome://resources chrome://theme 'unsafe-inline';";
 }
 
-std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyImgSrc()
-    const {
+std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyImgSrc() {
   return "img-src chrome://extension-icon chrome://theme chrome://resources "
       "data:;";
 }
diff --git a/chrome/browser/ui/webui/app_launcher_page_ui.h b/chrome/browser/ui/webui/app_launcher_page_ui.h
index 2bed0109..0b632d6a 100644
--- a/chrome/browser/ui/webui/app_launcher_page_ui.h
+++ b/chrome/browser/ui/webui/app_launcher_page_ui.h
@@ -37,17 +37,17 @@
     ~HTMLSource() override;
 
     // content::URLDataSource implementation.
-    std::string GetSource() const override;
+    std::string GetSource() override;
     void StartDataRequest(
         const std::string& path,
         const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
         const content::URLDataSource::GotDataCallback& callback) override;
-    std::string GetMimeType(const std::string&) const override;
-    bool ShouldReplaceExistingSource() const override;
-    bool AllowCaching() const override;
-    std::string GetContentSecurityPolicyScriptSrc() const override;
-    std::string GetContentSecurityPolicyStyleSrc() const override;
-    std::string GetContentSecurityPolicyImgSrc() const override;
+    std::string GetMimeType(const std::string&) override;
+    bool ShouldReplaceExistingSource() override;
+    bool AllowCaching() override;
+    std::string GetContentSecurityPolicyScriptSrc() override;
+    std::string GetContentSecurityPolicyStyleSrc() override;
+    std::string GetContentSecurityPolicyImgSrc() override;
 
    private:
 
diff --git a/chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_ui.cc b/chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_ui.cc
index 060cab4..5eaf0f3 100644
--- a/chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_ui.cc
@@ -175,16 +175,14 @@
   ~MobileSetupUIHTMLSource() override {}
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string&) const override {
-    return "text/html";
-  }
-  bool ShouldAddContentSecurityPolicy() const override { return false; }
-  bool AllowCaching() const override {
+  std::string GetMimeType(const std::string&) override { return "text/html"; }
+  bool ShouldAddContentSecurityPolicy() override { return false; }
+  bool AllowCaching() override {
     // Should not be cached to reflect dynamically-generated contents that may
     // depend on current settings.
     return false;
@@ -266,7 +264,7 @@
 
 MobileSetupUIHTMLSource::MobileSetupUIHTMLSource() : weak_ptr_factory_(this) {}
 
-std::string MobileSetupUIHTMLSource::GetSource() const {
+std::string MobileSetupUIHTMLSource::GetSource() {
   return chrome::kChromeUIMobileSetupHost;
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/image_source.cc b/chrome/browser/ui/webui/chromeos/image_source.cc
index 0c8e627..d5942fc 100644
--- a/chrome/browser/ui/webui/chromeos/image_source.cc
+++ b/chrome/browser/ui/webui/chromeos/image_source.cc
@@ -49,7 +49,7 @@
 ImageSource::~ImageSource() {
 }
 
-std::string ImageSource::GetSource() const {
+std::string ImageSource::GetSource() {
   return chrome::kChromeOSAssetHost;
 }
 
@@ -87,7 +87,7 @@
   }
 }
 
-std::string ImageSource::GetMimeType(const std::string& path) const {
+std::string ImageSource::GetMimeType(const std::string& path) {
   std::string mime_type;
   std::string ext = base::FilePath(path).Extension();
   if (!ext.empty())
diff --git a/chrome/browser/ui/webui/chromeos/image_source.h b/chrome/browser/ui/webui/chromeos/image_source.h
index 94f0deda1..5d7a5af 100644
--- a/chrome/browser/ui/webui/chromeos/image_source.h
+++ b/chrome/browser/ui/webui/chromeos/image_source.h
@@ -26,14 +26,14 @@
   ~ImageSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& got_data_callback)
       override;
 
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetMimeType(const std::string& path) override;
 
  private:
   // Continuation from StartDataRequest().
diff --git a/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc b/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc
index cafa6b84..07d906c 100644
--- a/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc
@@ -28,7 +28,7 @@
 SlowTraceSource::SlowTraceSource() {
 }
 
-std::string SlowTraceSource::GetSource() const {
+std::string SlowTraceSource::GetSource() {
   return chrome::kChromeUISlowTraceHost;
 }
 
@@ -51,7 +51,7 @@
                                    callback));
 }
 
-std::string SlowTraceSource::GetMimeType(const std::string& path) const {
+std::string SlowTraceSource::GetMimeType(const std::string& path) {
   return "application/zip";
 }
 
@@ -63,7 +63,7 @@
   callback.Run(trace_data.get());
 }
 
-bool SlowTraceSource::AllowCaching() const {
+bool SlowTraceSource::AllowCaching() {
   // Should not be cached to reflect dynamically-generated contents that may
   // depend on current settings.
   return false;
diff --git a/chrome/browser/ui/webui/chromeos/slow_trace_ui.h b/chrome/browser/ui/webui/chromeos/slow_trace_ui.h
index 495b05c..77a04463 100644
--- a/chrome/browser/ui/webui/chromeos/slow_trace_ui.h
+++ b/chrome/browser/ui/webui/chromeos/slow_trace_ui.h
@@ -29,13 +29,13 @@
   ~SlowTraceSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string& path) const override;
-  bool AllowCaching() const override;
+  std::string GetMimeType(const std::string& path) override;
+  bool AllowCaching() override;
 
  private:
   void OnGetTraceData(const content::URLDataSource::GotDataCallback& callback,
diff --git a/chrome/browser/ui/webui/chromeos/user_image_source.cc b/chrome/browser/ui/webui/chromeos/user_image_source.cc
index 1702ebc2..63c8676 100644
--- a/chrome/browser/ui/webui/chromeos/user_image_source.cc
+++ b/chrome/browser/ui/webui/chromeos/user_image_source.cc
@@ -173,7 +173,7 @@
 
 UserImageSource::~UserImageSource() {}
 
-std::string UserImageSource::GetSource() const {
+std::string UserImageSource::GetSource() {
   return chrome::kChromeUIUserImageHost;
 }
 
@@ -189,7 +189,7 @@
   callback.Run(GetUserImageInternal(account_id, frame));
 }
 
-std::string UserImageSource::GetMimeType(const std::string& path) const {
+std::string UserImageSource::GetMimeType(const std::string& path) {
   // We need to explicitly return a mime type, otherwise if the user tries to
   // drag the image they get no extension.
   return "image/png";
diff --git a/chrome/browser/ui/webui/chromeos/user_image_source.h b/chrome/browser/ui/webui/chromeos/user_image_source.h
index 287a21b..1e73aed 100644
--- a/chrome/browser/ui/webui/chromeos/user_image_source.h
+++ b/chrome/browser/ui/webui/chromeos/user_image_source.h
@@ -30,12 +30,12 @@
   ~UserImageSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetMimeType(const std::string& path) override;
 
   // Returns PNG encoded image for user with specified |account_id|. If there's
   // no user with such an id, returns the first default image. Always returns
diff --git a/chrome/browser/ui/webui/chromeos/video_source.cc b/chrome/browser/ui/webui/chromeos/video_source.cc
index c42955ad..4db1b33 100644
--- a/chrome/browser/ui/webui/chromeos/video_source.cc
+++ b/chrome/browser/ui/webui/chromeos/video_source.cc
@@ -62,7 +62,7 @@
 
 VideoSource::~VideoSource() {}
 
-std::string VideoSource::GetSource() const {
+std::string VideoSource::GetSource() {
   return chrome::kChromeOSAssetHost;
 }
 
@@ -85,7 +85,7 @@
                      got_data_callback));
 }
 
-std::string VideoSource::GetMimeType(const std::string& path) const {
+std::string VideoSource::GetMimeType(const std::string& path) {
   std::string mime_type;
   std::string ext = base::FilePath(path).Extension();
   if (!ext.empty())
diff --git a/chrome/browser/ui/webui/chromeos/video_source.h b/chrome/browser/ui/webui/chromeos/video_source.h
index 12a1a88..316509a 100644
--- a/chrome/browser/ui/webui/chromeos/video_source.h
+++ b/chrome/browser/ui/webui/chromeos/video_source.h
@@ -28,13 +28,13 @@
   ~VideoSource() override;
 
   // content::URLDataSource:
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& got_data_callback)
       override;
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetMimeType(const std::string& path) override;
 
  private:
   // Continuation from StartDataRequest().
diff --git a/chrome/browser/ui/webui/devtools_ui.cc b/chrome/browser/ui/webui/devtools_ui.cc
index 0643144..a9cc1d8 100644
--- a/chrome/browser/ui/webui/devtools_ui.cc
+++ b/chrome/browser/ui/webui/devtools_ui.cc
@@ -94,7 +94,7 @@
   ~DevToolsDataSource() override = default;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
 
   void StartDataRequest(
       const std::string& path,
@@ -105,10 +105,10 @@
   struct PendingRequest;
 
   // content::URLDataSource overrides.
-  std::string GetMimeType(const std::string& path) const override;
-  bool ShouldAddContentSecurityPolicy() const override;
-  bool ShouldDenyXFrameOptions() const override;
-  bool ShouldServeMimeTypeAsContentTypeHeader() const override;
+  std::string GetMimeType(const std::string& path) override;
+  bool ShouldAddContentSecurityPolicy() override;
+  bool ShouldDenyXFrameOptions() override;
+  bool ShouldServeMimeTypeAsContentTypeHeader() override;
 
   void OnLoadComplete(std::list<PendingRequest>::iterator request_iter,
                       std::unique_ptr<std::string> response_body);
@@ -158,7 +158,7 @@
   DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource);
 };
 
-std::string DevToolsDataSource::GetSource() const {
+std::string DevToolsDataSource::GetSource() {
   return chrome::kChromeUIDevToolsHost;
 }
 
@@ -235,19 +235,19 @@
   callback.Run(NULL);
 }
 
-std::string DevToolsDataSource::GetMimeType(const std::string& path) const {
+std::string DevToolsDataSource::GetMimeType(const std::string& path) {
   return GetMimeTypeForPath(path);
 }
 
-bool DevToolsDataSource::ShouldAddContentSecurityPolicy() const {
+bool DevToolsDataSource::ShouldAddContentSecurityPolicy() {
   return false;
 }
 
-bool DevToolsDataSource::ShouldDenyXFrameOptions() const {
+bool DevToolsDataSource::ShouldDenyXFrameOptions() {
   return false;
 }
 
-bool DevToolsDataSource::ShouldServeMimeTypeAsContentTypeHeader() const {
+bool DevToolsDataSource::ShouldServeMimeTypeAsContentTypeHeader() {
   return true;
 }
 
diff --git a/chrome/browser/ui/webui/discards/DEPS b/chrome/browser/ui/webui/discards/DEPS
new file mode 100644
index 0000000..f452a3a
--- /dev/null
+++ b/chrome/browser/ui/webui/discards/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "discards_ui.h": [
+    "+chrome/browser/performance_manager/webui_graph_dump.mojom.h",
+  ],
+}
diff --git a/chrome/browser/ui/webui/extensions/extension_icon_source.cc b/chrome/browser/ui/webui/extensions/extension_icon_source.cc
index eded33bb..618914d 100644
--- a/chrome/browser/ui/webui/extensions/extension_icon_source.cc
+++ b/chrome/browser/ui/webui/extensions/extension_icon_source.cc
@@ -102,11 +102,11 @@
   return ToBitmap(data, contents.length());
 }
 
-std::string ExtensionIconSource::GetSource() const {
+std::string ExtensionIconSource::GetSource() {
   return chrome::kChromeUIExtensionIconHost;
 }
 
-std::string ExtensionIconSource::GetMimeType(const std::string&) const {
+std::string ExtensionIconSource::GetMimeType(const std::string&) {
   // We need to explicitly return a mime type, otherwise if the user tries to
   // drag the image they get no extension.
   return "image/png";
@@ -139,7 +139,7 @@
   }
 }
 
-bool ExtensionIconSource::AllowCaching() const {
+bool ExtensionIconSource::AllowCaching() {
   // Should not be cached to reflect the latest contents that may be updated by
   // Extensions.
   return false;
diff --git a/chrome/browser/ui/webui/extensions/extension_icon_source.h b/chrome/browser/ui/webui/extensions/extension_icon_source.h
index 62cee4a..c7be8dc8 100644
--- a/chrome/browser/ui/webui/extensions/extension_icon_source.h
+++ b/chrome/browser/ui/webui/extensions/extension_icon_source.h
@@ -68,13 +68,13 @@
   static SkBitmap* LoadImageByResourceId(int resource_id);
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
-  std::string GetMimeType(const std::string&) const override;
+  std::string GetSource() override;
+  std::string GetMimeType(const std::string&) override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  bool AllowCaching() const override;
+  bool AllowCaching() override;
 
  private:
   // Encapsulates the request parameters for |request_id|.
diff --git a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
index f7449ee..e7b36ba 100644
--- a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
@@ -277,12 +277,11 @@
 
 ExtensionsInternalsSource::~ExtensionsInternalsSource() = default;
 
-std::string ExtensionsInternalsSource::GetSource() const {
+std::string ExtensionsInternalsSource::GetSource() {
   return chrome::kChromeUIExtensionsInternalsHost;
 }
 
-std::string ExtensionsInternalsSource::GetMimeType(
-    const std::string& path) const {
+std::string ExtensionsInternalsSource::GetMimeType(const std::string& path) {
   return "text/plain";
 }
 
diff --git a/chrome/browser/ui/webui/extensions/extensions_internals_source.h b/chrome/browser/ui/webui/extensions/extensions_internals_source.h
index c848a0a50..4a8a6ca 100644
--- a/chrome/browser/ui/webui/extensions/extensions_internals_source.h
+++ b/chrome/browser/ui/webui/extensions/extensions_internals_source.h
@@ -18,8 +18,8 @@
   ~ExtensionsInternalsSource() override;
 
   // content::URLDataSource:
-  std::string GetSource() const override;
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetSource() override;
+  std::string GetMimeType(const std::string& path) override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
diff --git a/chrome/browser/ui/webui/favicon_source.cc b/chrome/browser/ui/webui/favicon_source.cc
index c93c9e6..111a732 100644
--- a/chrome/browser/ui/webui/favicon_source.cc
+++ b/chrome/browser/ui/webui/favicon_source.cc
@@ -70,21 +70,17 @@
 }  // namespace
 
 FaviconSource::IconRequest::IconRequest()
-    : size_in_dip(gfx::kFaviconSize),
-      device_scale_factor(1.0f),
-      icon_request_origin(favicon::FaviconRequestOrigin::UNKNOWN) {}
+    : size_in_dip(gfx::kFaviconSize), device_scale_factor(1.0f) {}
 
 FaviconSource::IconRequest::IconRequest(
     const content::URLDataSource::GotDataCallback& cb,
     const GURL& path,
     int size,
-    float scale,
-    favicon::FaviconRequestOrigin origin)
+    float scale)
     : callback(cb),
       request_path(path),
       size_in_dip(size),
-      device_scale_factor(scale),
-      icon_request_origin(origin) {}
+      device_scale_factor(scale) {}
 
 FaviconSource::IconRequest::IconRequest(const IconRequest& other) = default;
 
@@ -97,7 +93,7 @@
 FaviconSource::~FaviconSource() {
 }
 
-std::string FaviconSource::GetSource() const {
+std::string FaviconSource::GetSource() {
   return chrome::kChromeUIFaviconHost;
 }
 
@@ -141,10 +137,10 @@
     // IconType.
     favicon_service->GetRawFavicon(
         url, favicon_base::IconType::kFavicon, desired_size_in_pixel,
-        base::BindRepeating(
-            &FaviconSource::OnFaviconDataAvailable, base::Unretained(this),
-            IconRequest(callback, url, parsed.size_in_dip,
-                        parsed.device_scale_factor, unsafe_request_origin)),
+        base::BindRepeating(&FaviconSource::OnFaviconDataAvailable,
+                            base::Unretained(this),
+                            IconRequest(callback, url, parsed.size_in_dip,
+                                        parsed.device_scale_factor)),
         &cancelable_task_tracker_);
   } else {
     // Intercept requests for prepopulated pages if TopSites exists.
@@ -167,10 +163,10 @@
         GetOpenTabsUIDelegate(profile_);
     favicon_request_handler_.GetRawFaviconForPageURL(
         url, desired_size_in_pixel,
-        base::BindOnce(
-            &FaviconSource::OnFaviconDataAvailable, base::Unretained(this),
-            IconRequest(callback, url, parsed.size_in_dip,
-                        parsed.device_scale_factor, unsafe_request_origin)),
+        base::BindOnce(&FaviconSource::OnFaviconDataAvailable,
+                       base::Unretained(this),
+                       IconRequest(callback, url, parsed.size_in_dip,
+                                   parsed.device_scale_factor)),
         unsafe_request_origin, favicon::FaviconRequestPlatform::kDesktop,
         favicon_service,
         LargeIconServiceFactory::GetForBrowserContext(profile_),
@@ -181,17 +177,17 @@
   }
 }
 
-std::string FaviconSource::GetMimeType(const std::string&) const {
+std::string FaviconSource::GetMimeType(const std::string&) {
   // We need to explicitly return a mime type, otherwise if the user tries to
   // drag the image they get no extension.
   return "image/png";
 }
 
-bool FaviconSource::AllowCaching() const {
+bool FaviconSource::AllowCaching() {
   return false;
 }
 
-bool FaviconSource::ShouldReplaceExistingSource() const {
+bool FaviconSource::ShouldReplaceExistingSource() {
   // Leave the existing DataSource in place, otherwise we'll drop any pending
   // requests on the floor.
   return false;
@@ -200,7 +196,7 @@
 bool FaviconSource::ShouldServiceRequest(
     const GURL& url,
     content::ResourceContext* resource_context,
-    int render_process_id) const {
+    int render_process_id) {
   if (url.SchemeIs(chrome::kChromeSearchScheme)) {
     return InstantIOContext::ShouldServiceRequest(url, resource_context,
                                                   render_process_id);
@@ -226,8 +222,7 @@
 
 void FaviconSource::SendDefaultResponse(
     const content::URLDataSource::GotDataCallback& callback) {
-  SendDefaultResponse(IconRequest(callback, GURL(), 16, 1.0f,
-                                  favicon::FaviconRequestOrigin::UNKNOWN));
+  SendDefaultResponse(IconRequest(callback, GURL(), 16, 1.0f));
 }
 
 void FaviconSource::SendDefaultResponse(const IconRequest& icon_request) {
diff --git a/chrome/browser/ui/webui/favicon_source.h b/chrome/browser/ui/webui/favicon_source.h
index 2c725b7..6eec8e5b 100644
--- a/chrome/browser/ui/webui/favicon_source.h
+++ b/chrome/browser/ui/webui/favicon_source.h
@@ -61,17 +61,17 @@
   ~FaviconSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string&) const override;
-  bool AllowCaching() const override;
-  bool ShouldReplaceExistingSource() const override;
+  std::string GetMimeType(const std::string&) override;
+  bool AllowCaching() override;
+  bool ShouldReplaceExistingSource() override;
   bool ShouldServiceRequest(const GURL& url,
                             content::ResourceContext* resource_context,
-                            int render_process_id) const override;
+                            int render_process_id) override;
 
  protected:
   struct IconRequest {
@@ -79,8 +79,7 @@
     IconRequest(const content::URLDataSource::GotDataCallback& cb,
                 const GURL& path,
                 int size,
-                float scale,
-                favicon::FaviconRequestOrigin origin);
+                float scale);
     IconRequest(const IconRequest& other);
     ~IconRequest();
 
@@ -88,7 +87,6 @@
     GURL request_path;
     int size_in_dip;
     float device_scale_factor;
-    favicon::FaviconRequestOrigin icon_request_origin;
   };
 
   // Exposed for testing.
diff --git a/chrome/browser/ui/webui/fileicon_source.cc b/chrome/browser/ui/webui/fileicon_source.cc
index b38de38..7706c0f 100644
--- a/chrome/browser/ui/webui/fileicon_source.cc
+++ b/chrome/browser/ui/webui/fileicon_source.cc
@@ -108,7 +108,7 @@
   }
 }
 
-std::string FileIconSource::GetSource() const {
+std::string FileIconSource::GetSource() {
   return kFileIconPath;
 }
 
@@ -123,12 +123,12 @@
   FetchFileIcon(file_path, scale_factor, icon_size, callback);
 }
 
-std::string FileIconSource::GetMimeType(const std::string&) const {
+std::string FileIconSource::GetMimeType(const std::string&) {
   // Rely on image decoder inferring the correct type.
   return std::string();
 }
 
-bool FileIconSource::AllowCaching() const {
+bool FileIconSource::AllowCaching() {
   return false;
 }
 
diff --git a/chrome/browser/ui/webui/fileicon_source.h b/chrome/browser/ui/webui/fileicon_source.h
index b8c093c..38fd779 100644
--- a/chrome/browser/ui/webui/fileicon_source.h
+++ b/chrome/browser/ui/webui/fileicon_source.h
@@ -25,13 +25,13 @@
   ~FileIconSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string&) const override;
-  bool AllowCaching() const override;
+  std::string GetMimeType(const std::string&) override;
+  bool AllowCaching() override;
 
  protected:
   // Once the |path| and |icon_size| has been determined from the request, this
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index d21cebf1..f60feb9 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -84,11 +84,11 @@
   ~InterstitialHTMLSource() override = default;
 
   // content::URLDataSource:
-  std::string GetMimeType(const std::string& mime_type) const override;
-  std::string GetSource() const override;
-  std::string GetContentSecurityPolicyScriptSrc() const override;
-  std::string GetContentSecurityPolicyStyleSrc() const override;
-  std::string GetContentSecurityPolicyImgSrc() const override;
+  std::string GetMimeType(const std::string& mime_type) override;
+  std::string GetSource() override;
+  std::string GetContentSecurityPolicyScriptSrc() override;
+  std::string GetContentSecurityPolicyStyleSrc() override;
+  std::string GetContentSecurityPolicyImgSrc() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
@@ -441,25 +441,24 @@
 
 // InterstitialHTMLSource
 
-std::string InterstitialHTMLSource::GetMimeType(
-    const std::string& mime_type) const {
+std::string InterstitialHTMLSource::GetMimeType(const std::string& mime_type) {
   return "text/html";
 }
 
-std::string InterstitialHTMLSource::GetSource() const {
+std::string InterstitialHTMLSource::GetSource() {
   return chrome::kChromeUIInterstitialHost;
 }
 
-std::string InterstitialHTMLSource::GetContentSecurityPolicyScriptSrc() const {
+std::string InterstitialHTMLSource::GetContentSecurityPolicyScriptSrc() {
   // 'unsafe-inline' is added to script-src.
   return "script-src chrome://resources 'self' 'unsafe-inline';";
 }
 
-std::string InterstitialHTMLSource::GetContentSecurityPolicyStyleSrc() const {
+std::string InterstitialHTMLSource::GetContentSecurityPolicyStyleSrc() {
   return "style-src 'self' 'unsafe-inline';";
 }
 
-std::string InterstitialHTMLSource::GetContentSecurityPolicyImgSrc() const {
+std::string InterstitialHTMLSource::GetContentSecurityPolicyImgSrc() {
   return "img-src data:;";
 }
 
diff --git a/chrome/browser/ui/webui/managed_ui_handler_unittest.cc b/chrome/browser/ui/webui/managed_ui_handler_unittest.cc
index 06fd62c..f9a4b1b 100644
--- a/chrome/browser/ui/webui/managed_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/managed_ui_handler_unittest.cc
@@ -4,12 +4,10 @@
 
 #include "chrome/browser/ui/webui/managed_ui_handler.h"
 
-#include "base/test/scoped_feature_list.h"
 #include "base/token.h"
 #include "base/values.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_service_impl.h"
@@ -35,8 +33,6 @@
   ManagedUIHandlerTest()
       : source_(content::TestWebUIDataSource::Create(
             base::Token::CreateRandom().ToString())) {
-    features_.InitAndEnableFeature(features::kShowManagedUi);
-
     // Create a TestingProfile that uses our MockConfigurationPolicyProvider.
     policy_provider()->Init();
     policy::PolicyServiceImpl::Providers providers = {policy_provider()};
@@ -80,7 +76,6 @@
 
  private:
   content::TestBrowserThreadBundle bundle_;
-  base::test::ScopedFeatureList features_;
 
   testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
   std::unique_ptr<TestingProfile> profile_;
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
index a99d5b78ca..5cc53f9c7 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -83,7 +83,7 @@
   void RegisterMessages() override;
 
  private:
-  network::mojom::NetworkContext* GetNetworkContext() const;
+  network::mojom::NetworkContext* GetNetworkContext();
 
   // Calls g_browser.receive in the renderer, passing in |command| and |arg|.
   // If the renderer is displaying a log file, the message will be ignored.
@@ -137,7 +137,7 @@
                                       bool succeeded);
 #endif
 
-  const content::WebUI* web_ui_;
+  content::WebUI* web_ui_;
 
   DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler);
 };
@@ -503,8 +503,8 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
-network::mojom::NetworkContext* NetInternalsMessageHandler::GetNetworkContext()
-    const {
+network::mojom::NetworkContext*
+NetInternalsMessageHandler::GetNetworkContext() {
   return content::BrowserContext::GetDefaultStoragePartition(
              web_ui_->GetWebContents()->GetBrowserContext())
       ->GetNetworkContext();
diff --git a/chrome/browser/ui/webui/ntp/new_tab_ui.cc b/chrome/browser/ui/webui/ntp/new_tab_ui.cc
index f565648..cfef0e77 100644
--- a/chrome/browser/ui/webui/ntp/new_tab_ui.cc
+++ b/chrome/browser/ui/webui/ntp/new_tab_ui.cc
@@ -169,7 +169,7 @@
     : profile_(profile) {
 }
 
-std::string NewTabUI::NewTabHTMLSource::GetSource() const {
+std::string NewTabUI::NewTabHTMLSource::GetSource() {
   return chrome::kChromeUINewTabHost;
 }
 
@@ -199,35 +199,31 @@
   callback.Run(html_bytes.get());
 }
 
-std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string& resource)
-    const {
+std::string NewTabUI::NewTabHTMLSource::GetMimeType(
+    const std::string& resource) {
   return "text/html";
 }
 
-bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const {
+bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() {
   return false;
 }
 
-std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyScriptSrc()
-    const {
+std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyScriptSrc() {
   // 'unsafe-inline' and google resources are added to script-src.
   return "script-src chrome://resources 'self' 'unsafe-eval' 'unsafe-inline' "
       "*.google.com *.gstatic.com;";
 }
 
-std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyStyleSrc()
-    const {
+std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyStyleSrc() {
   return "style-src 'self' chrome://resources 'unsafe-inline' chrome://theme;";
 }
 
-std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyImgSrc()
-    const {
+std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyImgSrc() {
   return "img-src chrome-search://thumb chrome-search://thumb2 "
       "chrome-search://theme chrome://theme data:;";
 }
 
-std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyChildSrc()
-    const {
+std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyChildSrc() {
   return "child-src chrome-search://most-visited;";
 }
 
diff --git a/chrome/browser/ui/webui/ntp/new_tab_ui.h b/chrome/browser/ui/webui/ntp/new_tab_ui.h
index 53f6473e..63d4d59 100644
--- a/chrome/browser/ui/webui/ntp/new_tab_ui.h
+++ b/chrome/browser/ui/webui/ntp/new_tab_ui.h
@@ -55,17 +55,17 @@
     ~NewTabHTMLSource() override;
 
     // content::URLDataSource implementation.
-    std::string GetSource() const override;
+    std::string GetSource() override;
     void StartDataRequest(
         const std::string& path,
         const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
         const content::URLDataSource::GotDataCallback& callback) override;
-    std::string GetMimeType(const std::string&) const override;
-    bool ShouldReplaceExistingSource() const override;
-    std::string GetContentSecurityPolicyScriptSrc() const override;
-    std::string GetContentSecurityPolicyStyleSrc() const override;
-    std::string GetContentSecurityPolicyImgSrc() const override;
-    std::string GetContentSecurityPolicyChildSrc() const override;
+    std::string GetMimeType(const std::string&) override;
+    bool ShouldReplaceExistingSource() override;
+    std::string GetContentSecurityPolicyScriptSrc() override;
+    std::string GetContentSecurityPolicyStyleSrc() override;
+    std::string GetContentSecurityPolicyImgSrc() override;
+    std::string GetContentSecurityPolicyChildSrc() override;
 
    private:
     // Pointer back to the original profile.
diff --git a/chrome/browser/ui/webui/prefs_internals_source.cc b/chrome/browser/ui/webui/prefs_internals_source.cc
index 9d30d11..382f02f 100644
--- a/chrome/browser/ui/webui/prefs_internals_source.cc
+++ b/chrome/browser/ui/webui/prefs_internals_source.cc
@@ -19,11 +19,11 @@
 
 PrefsInternalsSource::~PrefsInternalsSource() = default;
 
-std::string PrefsInternalsSource::GetSource() const {
+std::string PrefsInternalsSource::GetSource() {
   return chrome::kChromeUIPrefsInternalsHost;
 }
 
-std::string PrefsInternalsSource::GetMimeType(const std::string& path) const {
+std::string PrefsInternalsSource::GetMimeType(const std::string& path) {
   return "text/plain";
 }
 
diff --git a/chrome/browser/ui/webui/prefs_internals_source.h b/chrome/browser/ui/webui/prefs_internals_source.h
index 471c05d..d20af55 100644
--- a/chrome/browser/ui/webui/prefs_internals_source.h
+++ b/chrome/browser/ui/webui/prefs_internals_source.h
@@ -17,8 +17,8 @@
   ~PrefsInternalsSource() override;
 
   // content::URLDataSource:
-  std::string GetSource() const override;
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetSource() override;
+  std::string GetMimeType(const std::string& path) override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 47528eac..a1ff2e4 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -284,6 +284,10 @@
 const char kCloudPrintURL[] = "cloudPrintURL";
 // Name of a dictionary field holding the signed in user accounts.
 const char kUserAccounts[] = "userAccounts";
+// Name of a dictionary field indicating whether sync is available. If false,
+// Print Preview will always send a request to the Google Cloud Print server on
+// load, to check the user's sign in state.
+const char kSyncAvailable[] = "syncAvailable";
 
 // Get the print job settings dictionary from |json_str|.
 // Returns |base::Value()| on failure.
@@ -861,8 +865,19 @@
   CHECK(args->GetBoolean(0, &add_account));
 
   chrome::ScopedTabbedBrowserDisplayer displayer(Profile::FromWebUI(web_ui()));
-  print_dialog_cloud::CreateCloudPrintSigninTab(displayer.browser(),
-                                                add_account);
+  print_dialog_cloud::CreateCloudPrintSigninTab(
+      displayer.browser(), add_account,
+      base::BindOnce(&PrintPreviewHandler::OnSignInTabClosed,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void PrintPreviewHandler::OnSignInTabClosed() {
+  if (identity_manager_) {
+    // Sign in state will be reported in OnAccountsInCookieJarUpdated, so no
+    // need to do anything here.
+    return;
+  }
+  FireWebUIListener("check-for-account-update");
 }
 
 #if defined(OS_CHROMEOS)
@@ -960,6 +975,9 @@
     for (const gaia::ListedAccount& account : accounts) {
       account_list.GetList().emplace_back(account.email);
     }
+    settings->SetKey(kSyncAvailable, base::Value(true));
+  } else {
+    settings->SetKey(kSyncAvailable, base::Value(false));
   }
   settings->SetKey(kUserAccounts, std::move(account_list));
 }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 47856437f..1b34ac7 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -225,6 +225,9 @@
   // a boolean indicating whether the user is adding an account.
   void HandleSignin(const base::ListValue* args);
 
+  // Called when the tab opened by HandleSignIn() is closed.
+  void OnSignInTabClosed();
+
 #if defined(OS_CHROMEOS)
   // Generates new token and sends back to UI.
   void HandleGetAccessToken(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
index efbbb97..64af66f 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -376,6 +376,8 @@
         settings->FindKeyOfType("cloudPrintURL", base::Value::Type::STRING));
     ASSERT_TRUE(
         settings->FindKeyOfType("userAccounts", base::Value::Type::LIST));
+    ASSERT_TRUE(
+        settings->FindKeyOfType("syncAvailable", base::Value::Type::BOOLEAN));
   }
 
   IPC::TestSink& initiator_sink() {
diff --git a/chrome/browser/ui/webui/theme_source.cc b/chrome/browser/ui/webui/theme_source.cc
index a1d4d244..927c4b9a 100644
--- a/chrome/browser/ui/webui/theme_source.cc
+++ b/chrome/browser/ui/webui/theme_source.cc
@@ -73,7 +73,7 @@
 
 ThemeSource::~ThemeSource() = default;
 
-std::string ThemeSource::GetSource() const {
+std::string ThemeSource::GetSource() {
   return chrome::kChromeUIThemeHost;
 }
 
@@ -157,14 +157,14 @@
   }
 }
 
-std::string ThemeSource::GetMimeType(const std::string& path) const {
+std::string ThemeSource::GetMimeType(const std::string& path) {
   std::string parsed_path;
   webui::ParsePathAndScale(GetThemeUrl(path), &parsed_path, nullptr);
   return IsNewTabCssPath(parsed_path) ? "text/css" : "image/png";
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
-ThemeSource::TaskRunnerForRequestPath(const std::string& path) const {
+ThemeSource::TaskRunnerForRequestPath(const std::string& path) {
   std::string parsed_path;
   webui::ParsePathAndScale(GetThemeUrl(path), &parsed_path, nullptr);
 
@@ -181,14 +181,14 @@
              : nullptr;
 }
 
-bool ThemeSource::AllowCaching() const {
+bool ThemeSource::AllowCaching() {
   return false;
 }
 
 bool ThemeSource::ShouldServiceRequest(
     const GURL& url,
     content::ResourceContext* resource_context,
-    int render_process_id) const {
+    int render_process_id) {
   return url.SchemeIs(chrome::kChromeSearchScheme)
              ? InstantIOContext::ShouldServiceRequest(url, resource_context,
                                                       render_process_id)
diff --git a/chrome/browser/ui/webui/theme_source.h b/chrome/browser/ui/webui/theme_source.h
index 724e01f..aa323d6 100644
--- a/chrome/browser/ui/webui/theme_source.h
+++ b/chrome/browser/ui/webui/theme_source.h
@@ -21,18 +21,18 @@
   ~ThemeSource() override;
 
   // content::URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetMimeType(const std::string& path) override;
   scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerForRequestPath(
-      const std::string& path) const override;
-  bool AllowCaching() const override;
+      const std::string& path) override;
+  bool AllowCaching() override;
   bool ShouldServiceRequest(const GURL& url,
                             content::ResourceContext* resource_context,
-                            int render_process_id) const override;
+                            int render_process_id) override;
 
  private:
   // Fetches and sends the theme bitmap.
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
index 53148f2..e056112 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
+++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -14,6 +15,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
+#include "base/rand_util.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
@@ -22,8 +24,10 @@
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/update_engine_client.h"
+#include "chromeos/settings/timezone_settings.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
 
 using chromeos::DBusThreadManager;
 using chromeos::UpdateEngineClient;
@@ -118,7 +122,6 @@
       upgrade_notification_timer_(tick_clock),
       initialized_(false),
       weak_factory_(this) {
-  CalculateThresholds();
   // Not all tests provide a PrefService for local_state().
   PrefService* local_state = g_browser_process->local_state();
   if (local_state) {
@@ -157,14 +160,11 @@
 }
 
 base::TimeDelta UpgradeDetectorChromeos::GetHighAnnoyanceLevelDelta() {
-  return high_threshold_ - elevated_threshold_;
+  return high_deadline_ - elevated_deadline_;
 }
 
 base::Time UpgradeDetectorChromeos::GetHighAnnoyanceDeadline() {
-  const base::Time detected_time = upgrade_detected_time();
-  if (detected_time.is_null())
-    return detected_time;
-  return detected_time + high_threshold_;
+  return high_deadline_;
 }
 
 // static
@@ -183,15 +183,63 @@
   return base::TimeDelta::FromMilliseconds(value);
 }
 
-void UpgradeDetectorChromeos::CalculateThresholds() {
+// static
+base::TimeDelta UpgradeDetectorChromeos::GenRandomTimeDelta(
+    base::TimeDelta max) {
+  return max * base::RandDouble();
+}
+
+// static
+base::Time UpgradeDetectorChromeos::AdjustDeadline(base::Time deadline) {
+  // Compute the offset applied to GMT to get local time at |deadline|.
+  const icu::TimeZone& time_zone =
+      chromeos::system::TimezoneSettings::GetInstance()->GetTimezone();
+  UErrorCode status = U_ZERO_ERROR;
+  int32_t raw_offset, dst_offset;
+  time_zone.getOffset(deadline.ToDoubleT() * base::Time::kMillisecondsPerSecond,
+                      true /* local */, raw_offset, dst_offset, status);
+  base::TimeDelta time_zone_offset;
+  if (U_FAILURE(status)) {
+    LOG(ERROR) << "Failed to get time zone offset, error code: " << status;
+    // The fallback case is to get the raw timezone offset ignoring the daylight
+    // saving time.
+    time_zone_offset =
+        base::TimeDelta::FromMilliseconds(time_zone.getRawOffset());
+  } else {
+    time_zone_offset =
+        base::TimeDelta::FromMilliseconds(raw_offset + dst_offset);
+  }
+
+  // To get local midnight add timezone offset to deadline and treat this time
+  // as UTC based to use UTCMidnight(), then subtract timezone offset.
+  auto midnight =
+      (deadline + time_zone_offset).UTCMidnight() - time_zone_offset;
+  const auto day_time = deadline - midnight;
+  // Return the exact deadline if it naturally falls between 2am and 4am.
+  if (day_time >= base::TimeDelta::FromHours(2) &&
+      day_time <= base::TimeDelta::FromHours(4)) {
+    return deadline;
+  }
+  // Advance to the next day if the deadline falls after 4am.
+  if (day_time > base::TimeDelta::FromHours(4))
+    midnight += base::TimeDelta::FromDays(1);
+
+  return midnight + base::TimeDelta::FromHours(2) +
+         GenRandomTimeDelta(base::TimeDelta::FromHours(2));
+}
+
+void UpgradeDetectorChromeos::CalculateDeadlines() {
   base::TimeDelta notification_period = GetRelaunchNotificationPeriod();
-  high_threshold_ = notification_period.is_zero() ? kDefaultHighThreshold
-                                                  : notification_period;
+  if (notification_period.is_zero())
+    notification_period = kDefaultHighThreshold;
+  high_deadline_ =
+      AdjustDeadline(upgrade_detected_time() + notification_period);
+
   base::TimeDelta heads_up_period = GetRelaunchHeadsUpPeriod();
   if (heads_up_period.is_zero())
     heads_up_period = kDefaultHeadsUpPeriod;
-  elevated_threshold_ =
-      high_threshold_ - std::min(heads_up_period, high_threshold_);
+  elevated_deadline_ =
+      std::max(high_deadline_ - heads_up_period, upgrade_detected_time());
 }
 
 void UpgradeDetectorChromeos::OnRelaunchHeadsUpPeriodPrefChanged() {
@@ -215,8 +263,10 @@
 void UpgradeDetectorChromeos::UpdateStatusChanged(
     const UpdateEngineClient::Status& status) {
   if (status.status == UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
-    if (upgrade_detected_time().is_null())
+    if (upgrade_detected_time().is_null()) {
       set_upgrade_detected_time(clock()->Now());
+      CalculateDeadlines();
+    }
 
     if (status.is_rollback) {
       // Powerwash will be required, determine what kind of notification to show
@@ -246,32 +296,33 @@
 void UpgradeDetectorChromeos::OnThresholdPrefChanged() {
   // Check the current stage and potentially notify observers now if a change to
   // the observed policies results in changes to the thresholds.
-  const base::TimeDelta old_elevated_threshold = elevated_threshold_;
-  const base::TimeDelta old_high_threshold = high_threshold_;
-  CalculateThresholds();
-  if (!upgrade_detected_time().is_null() &&
-      (elevated_threshold_ != old_elevated_threshold ||
-       high_threshold_ != old_high_threshold)) {
+  if (upgrade_detected_time().is_null())
+    return;
+  const base::Time old_elevated_deadline = elevated_deadline_;
+  const base::Time old_high_deadline = high_deadline_;
+  CalculateDeadlines();
+  if (elevated_deadline_ != old_elevated_deadline ||
+      high_deadline_ != old_high_deadline) {
     NotifyOnUpgrade();
   }
 }
 
 void UpgradeDetectorChromeos::NotifyOnUpgrade() {
-  const base::TimeDelta delta = clock()->Now() - upgrade_detected_time();
+  const base::Time current_time = clock()->Now();
   // The delay from now until the next highest notification stage is reached, or
   // zero if the highest notification stage has been reached.
   base::TimeDelta next_delay;
 
   const auto last_stage = upgrade_notification_stage();
   // These if statements must be sorted (highest interval first).
-  if (delta >= high_threshold_) {
+  if (current_time >= high_deadline_) {
     set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH);
-  } else if (delta >= elevated_threshold_) {
+  } else if (current_time >= elevated_deadline_) {
     set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED);
-    next_delay = high_threshold_ - delta;
+    next_delay = high_deadline_ - current_time;
   } else {
     set_upgrade_notification_stage(UPGRADE_ANNOYANCE_NONE);
-    next_delay = elevated_threshold_ - delta;
+    next_delay = elevated_deadline_ - current_time;
   }
   const auto new_stage = upgrade_notification_stage();
 
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
index e2e9885a..a87323d 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
+++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
@@ -49,24 +49,29 @@
   UpgradeDetectorChromeos(const base::Clock* clock,
                           const base::TickClock* tick_clock);
 
+  // Return adjusted high annoyance deadline which takes place at night between
+  // 2am and 4am. If |deadline| takes place after 4am it is prolonged for the
+  // next day night between 2am and 4am.
+  static base::Time AdjustDeadline(base::Time deadline);
+
  private:
   friend class base::NoDestructor<UpgradeDetectorChromeos>;
 
+  // Return random TimeDelta uniformly selected between zero and |max|.
+  static base::TimeDelta GenRandomTimeDelta(base::TimeDelta max);
+
   // Returns the period between first notification and Recommended / Required
   // deadline specified via the RelaunchHeadsUpPeriod policy setting, or a
   // zero delta if unset or out of range.
   static base::TimeDelta GetRelaunchHeadsUpPeriod();
 
-  // Calculates |elevated_threshold_| and |high_threshold_|.
-  void CalculateThresholds();
+  // Calculates |elevated_deadline_| and |high_deadline_|.
+  void CalculateDeadlines();
 
   // Handles a change to the browser.relaunch_heads_up_period Local State
   // preference. Calls NotifyUpgrade if an upgrade is available.
   void OnRelaunchHeadsUpPeriodPrefChanged();
 
-  // Returns the threshold to reach high annoyance level.
-  static base::TimeDelta DetermineHighThreshold();
-
   // UpgradeDetector:
   void OnRelaunchNotificationPeriodPrefChanged() override;
 
@@ -86,11 +91,11 @@
   void OnChannelsReceived(std::string current_channel,
                           std::string target_channel);
 
-  // The delta from upgrade detection until elevated annoyance level is reached.
-  base::TimeDelta elevated_threshold_;
+  // The time when elevated annoyance deadline is reached.
+  base::Time elevated_deadline_;
 
-  // The delta from upgrade detection until high annoyance level is reached.
-  base::TimeDelta high_threshold_;
+  // The time when high annoyance deadline is reached.
+  base::Time high_deadline_;
 
   // Observes changes to the browser.relaunch_heads_up_period Local State
   // preference.
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc b/chrome/browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc
index 8c29afe..1b5860f 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc
+++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc
@@ -20,9 +20,11 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_update_engine_client.h"
+#include "chromeos/settings/timezone_settings.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
 
 namespace {
 
@@ -34,6 +36,7 @@
   ~TestUpgradeDetectorChromeos() override = default;
 
   // Exposed for testing.
+  using UpgradeDetectorChromeos::AdjustDeadline;
   using UpgradeDetectorChromeos::UPGRADE_AVAILABLE_REGULAR;
 
   DISALLOW_COPY_AND_ASSIGN(TestUpgradeDetectorChromeos);
@@ -63,7 +66,8 @@
 class UpgradeDetectorChromeosTest : public ::testing::Test {
  protected:
   UpgradeDetectorChromeosTest()
-      : scoped_task_environment_(
+      : utc_(icu::TimeZone::createTimeZone("Etc/GMT")),
+        scoped_task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
         scoped_local_state_(TestingBrowserProcess::GetGlobal()) {
     // Disable the detector's check to see if autoupdates are inabled.
@@ -78,6 +82,10 @@
     dbus_setter->SetUpdateEngineClient(
         std::unique_ptr<chromeos::UpdateEngineClient>(
             fake_update_engine_client_));
+    // Set UTC timezone
+    chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(*utc_);
+    // Fast forward to align deadline be at 2am
+    FastForwardBy(base::TimeDelta::FromHours(2));
   }
 
   const base::Clock* GetMockClock() {
@@ -132,6 +140,7 @@
   }
 
  private:
+  std::unique_ptr<icu::TimeZone> utc_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   ScopedTestingLocalState scoped_local_state_;
 
@@ -359,3 +368,59 @@
   upgrade_detector.Shutdown();
   RunUntilIdle();
 }
+
+TEST_F(UpgradeDetectorChromeosTest, TimezoneAdjustment) {
+  TestUpgradeDetectorChromeos upgrade_detector(GetMockClock(),
+                                               GetMockTickClock());
+  upgrade_detector.Init();
+  const auto delta = base::TimeDelta::FromDays(7);
+
+  // Europe/Moscow timezone
+  std::unique_ptr<icu::TimeZone> msk_timezone(
+      icu::TimeZone::createTimeZone("Europe/Moscow"));
+  chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(*msk_timezone);
+  base::Time detect_time;
+  ASSERT_TRUE(
+      base::Time::FromString("1 Jan 2018 06:00 UTC+0300", &detect_time));
+  base::Time deadline, deadline_lower_border, deadline_upper_border;
+  ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC+0300",
+                                     &deadline_lower_border));
+  ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC+0300",
+                                     &deadline_upper_border));
+  deadline = upgrade_detector.AdjustDeadline(detect_time + delta);
+  EXPECT_GE(deadline, deadline_lower_border);
+  EXPECT_LE(deadline, deadline_upper_border);
+
+  // Pacific/Midway timezone
+  std::unique_ptr<icu::TimeZone> midway_timezone(
+      icu::TimeZone::createTimeZone("Pacific/Midway"));
+  chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(
+      *midway_timezone);
+  ASSERT_TRUE(
+      base::Time::FromString("1 Jan 2018 23:00 UTC-1100", &detect_time));
+  ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC-1100",
+                                     &deadline_lower_border));
+  ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC-1100",
+                                     &deadline_upper_border));
+  deadline = upgrade_detector.AdjustDeadline(detect_time + delta);
+  EXPECT_GE(deadline, deadline_lower_border);
+  EXPECT_LE(deadline, deadline_upper_border);
+
+  // Pacific/Kiritimati timezone
+  std::unique_ptr<icu::TimeZone> kiritimati_timezone(
+      icu::TimeZone::createTimeZone("Pacific/Kiritimati"));
+  chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(
+      *kiritimati_timezone);
+  ASSERT_TRUE(
+      base::Time::FromString("1 Jan 2018 16:30 UTC+1400", &detect_time));
+  ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC+1400",
+                                     &deadline_lower_border));
+  ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC+1400",
+                                     &deadline_upper_border));
+  deadline = upgrade_detector.AdjustDeadline(detect_time + delta);
+  EXPECT_GE(deadline, deadline_lower_border);
+  EXPECT_LE(deadline, deadline_upper_border);
+
+  upgrade_detector.Shutdown();
+  RunUntilIdle();
+}
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 81c9570..0d6a3538 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -242,6 +242,7 @@
     "//components/gcm_driver/common",
     "//components/metrics",
     "//components/metrics:net",
+    "//components/metrics/public/interfaces:call_stack_mojo_bindings",
     "//components/nacl/common:buildflags",
     "//components/nacl/common:process_type",
     "//components/nacl/common:switches",
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index 4d4b7b05..5248be4 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -26,6 +26,7 @@
   "+components/metrics/client_info.h",
   "+components/metrics/metadata_recorder.h",
   "+components/metrics/metrics_pref_names.h",
+  "+components/metrics/public",
   "+components/nacl/common",
   "+components/net_log",
   "+components/network_session_configurator/common",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index d39a1fa..188efd4f 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -376,6 +376,11 @@
 };
 #endif  // !defined(OS_ANDROID)
 
+#if defined(OS_CHROMEOS)
+const base::Feature kKernelnextVMs{"KernelnextVMs",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 // Uses KidsManagement UrlClassification instead of SafeSearch for supervised
 // accounts.
 const base::Feature kKidsManagementUrlClassification{
@@ -535,11 +540,6 @@
 const base::Feature kSecurityKeyAttestationPrompt{
     "SecurityKeyAttestationPrompt", base::FEATURE_ENABLED_BY_DEFAULT};
 
-#if !defined(OS_ANDROID)
-const base::Feature kShowManagedUi{"ShowManagedUi",
-                                   base::FEATURE_ENABLED_BY_DEFAULT};
-#endif
-
 #if defined(OS_ANDROID)
 const base::Feature kShowTrustedPublisherURL{"ShowTrustedPublisherURL",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 904bbe0..dd0b3bd 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -240,6 +240,11 @@
 extern const base::Feature kIntentPicker;
 #endif
 
+#if defined(OS_CHROMEOS)
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kKernelnextVMs;
+#endif
+
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kKidsManagementUrlClassification;
 
@@ -348,10 +353,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kSecurityKeyAttestationPrompt;
 
-#if !defined(OS_ANDROID)
-COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kShowManagedUi;
-#endif
-
 #if defined(OS_ANDROID)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kShowTrustedPublisherURL;
diff --git a/chrome/common/thread_profiler.cc b/chrome/common/thread_profiler.cc
index 8f8f783..5b480c8 100644
--- a/chrome/common/thread_profiler.cc
+++ b/chrome/common/thread_profiler.cc
@@ -22,7 +22,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/embedder/switches.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 using CallStackProfileBuilder = metrics::CallStackProfileBuilder;
 using CallStackProfileParams = metrics::CallStackProfileParams;
@@ -187,18 +186,14 @@
 }
 
 // static
-void ThreadProfiler::SetServiceManagerConnectorForChildProcess(
-    service_manager::Connector* connector) {
+void ThreadProfiler::SetCollectorForChildProcess(
+    mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector) {
   if (!StackSamplingConfiguration::Get()->IsProfilerEnabledForCurrentProcess())
     return;
 
   DCHECK_NE(CallStackProfileParams::BROWSER_PROCESS, GetProcess());
-
-  metrics::mojom::CallStackProfileCollectorPtr browser_interface;
-  connector->BindInterface(content::mojom::kSystemServiceName,
-                           &browser_interface);
   CallStackProfileBuilder::SetParentProfileCollectorForChildProcess(
-      std::move(browser_interface));
+      metrics::mojom::CallStackProfileCollectorPtr(std::move(collector)));
 }
 
 // ThreadProfiler implementation synopsis:
diff --git a/chrome/common/thread_profiler.h b/chrome/common/thread_profiler.h
index 4abf2e2..c1793e70 100644
--- a/chrome/common/thread_profiler.h
+++ b/chrome/common/thread_profiler.h
@@ -16,12 +16,10 @@
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "components/metrics/call_stack_profile_params.h"
+#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/metrics_proto/sampled_profile.pb.h"
 
-namespace service_manager {
-class Connector;
-}
-
 // PeriodicSamplingScheduler repeatedly schedules periodic sampling of the
 // thread through calls to GetTimeToNextCollection(). This class is exposed
 // to allow testing.
@@ -95,8 +93,8 @@
   // exposed to the child process, and metrics::mojom::CallStackProfileCollector
   // declared in chrome_content_browser_manifest_overlay.json, for the binding
   // to succeed.
-  static void SetServiceManagerConnectorForChildProcess(
-      service_manager::Connector* connector);
+  static void SetCollectorForChildProcess(
+      mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector);
 
  private:
   class WorkIdRecorder;
diff --git a/chrome/gpu/chrome_content_gpu_client.cc b/chrome/gpu/chrome_content_gpu_client.cc
index 02e80a8f..9e320e5 100644
--- a/chrome/gpu/chrome_content_gpu_client.cc
+++ b/chrome/gpu/chrome_content_gpu_client.cc
@@ -14,7 +14,9 @@
 #include "build/build_config.h"
 #include "content/public/child/child_thread.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/service_names.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/service_manager/public/cpp/connector.h"
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 #include "media/cdm/cdm_paths.h"
@@ -90,8 +92,12 @@
           switches::kInProcessGPU)) {
     main_thread_profiler_->SetMainThreadTaskRunner(
         base::ThreadTaskRunnerHandle::Get());
-    ThreadProfiler::SetServiceManagerConnectorForChildProcess(
-        content::ChildThread::Get()->GetConnector());
+
+    mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector;
+    content::ChildThread::Get()->GetConnector()->Connect(
+        content::mojom::kSystemServiceName,
+        collector.InitWithNewPipeAndPassReceiver());
+    ThreadProfiler::SetCollectorForChildProcess(std::move(collector));
   }
 }
 
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 4f04b84..bb33d93 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -435,8 +435,15 @@
           switches::kSingleProcess)) {
     main_thread_profiler_->SetMainThreadTaskRunner(
         base::ThreadTaskRunnerHandle::Get());
-    ThreadProfiler::SetServiceManagerConnectorForChildProcess(
-        thread->GetConnector());
+    mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector;
+    service_manager::Connector* connector = nullptr;
+    if (content::ChildThread::Get())
+      connector = content::ChildThread::Get()->GetConnector();
+    if (connector) {
+      connector->Connect(content::mojom::kBrowserServiceName,
+                         collector.InitWithNewPipeAndPassReceiver());
+      ThreadProfiler::SetCollectorForChildProcess(std::move(collector));
+    }
   }
 }
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8a74cfb2..6685d0c6 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -464,18 +464,35 @@
     android_manifest_dep = ":android_browsertests_manifest"
 
     deps = [
+      ":android_browsertests_assets",
       ":android_browsertests_java",
+      ":browser_tests_runner",
       ":test_support",
       ":test_support_ui_android",
       "//chrome:chrome_android_core",
       "//chrome/android:app_hooks_java",
       "//chrome/android:chrome_java",
+
+      # TODO(crbug.com/961849): This is needed for ShellManager which is what
+      # the ChromeBrowserTestsActivity is using to build the java UI. It's
+      # likely we want to replace it with something new that uses ChromeWindow
+      # and CompositorViewHolder instead.
+      "//content/shell:content_shell_lib",
       "//content/test:android_test_message_pump_support",
+      "//testing/android/native_test:native_test_support",
     ]
 
     sources = [
       "android/browsertests_apk/android_browsertests_jni_onload.cc",
-      # TODO(crbug.com/961849): Write an Android browser test and put it here.
+      "base/android/android_browser_test_browsertest_android.cc",
+    ]
+
+    data = [
+      "$root_gen_dir/chrome/android/chrome_apk_paks/chrome_100_percent.pak",
+      "$root_gen_dir/chrome/android/chrome_apk_paks/locales/en-US.pak",
+      "$root_gen_dir/chrome/android/chrome_apk_paks/resources.pak",
+      "$root_gen_dir/components/components_resources.pak",
+      "$root_out_dir/browser_tests.pak",
     ]
   }
 
@@ -484,16 +501,46 @@
     testonly = true
 
     sources = [
-      # TODO(crbug.com/961849): Write an AndroidBrowserTest base class and put
-      # it here.
+      "base/android/android_browser_test.cc",
+      "base/android/android_browser_test.h",
     ]
-    public_deps = []
+    public_deps = [
+      "//content/test:test_support",
+    ]
     deps = [
       "//chrome/browser",
       "//content/public/browser",
     ]
   }
 
+  android_assets("android_browsertests_assets") {
+    testonly = true
+    sources = []
+    deps = []
+
+    # These are grit() rules so they are in $root_gen_dir.
+    deps += [
+      "//chrome/android:chrome_apk_paks",
+      "//components/resources",
+    ]
+    sources += [
+      "$root_gen_dir/chrome/android/chrome_apk_paks/chrome_100_percent.pak",
+      "$root_gen_dir/chrome/android/chrome_apk_paks/locales/en-US.pak",
+      "$root_gen_dir/chrome/android/chrome_apk_paks/resources.pak",
+      "$root_gen_dir/components/components_resources.pak",
+    ]
+
+    # These are repack() rules so they are in $root_out_dir.
+    deps += [ "//chrome:browser_tests_pak" ]
+    sources += [ "$root_out_dir/browser_tests.pak" ]
+
+    if (use_v8_context_snapshot) {
+      deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
+    } else {
+      deps += [ "//v8:v8_external_startup_data_assets" ]
+    }
+  }
+
   android_library("android_browsertests_java") {
     testonly = true
     deps = [
@@ -501,6 +548,7 @@
       "//base:base_java",
       "//base:base_java_test_support",
       "//chrome/android:chrome_java",
+      "//content/public/test/android:android_test_message_pump_support_java",
       "//content/shell/android:content_shell_browsertests_java",
       "//testing/android/native_test:native_test_java",
     ]
@@ -524,26 +572,26 @@
   }
 }
 
-if (!is_android) {
-  static_library("browser_tests_runner") {
-    testonly = true
-    sources = []
+static_library("browser_tests_runner") {
+  testonly = true
+  sources = []
 
-    deps = [
-      ":test_support",
-    ]
+  deps = [
+    ":test_support",
+  ]
 
-    if (is_chromeos) {
-      sources += [ "base/browser_tests_main_chromeos.cc" ]
-    } else {
-      sources += [ "base/browser_tests_main.cc" ]
-    }
-
-    if (is_win) {
-      deps += [ "//chrome/installer/util:strings" ]
-    }
+  if (is_chromeos) {
+    sources += [ "base/browser_tests_main_chromeos.cc" ]
+  } else {
+    sources += [ "base/browser_tests_main.cc" ]
   }
 
+  if (is_win) {
+    deps += [ "//chrome/installer/util:strings" ]
+  }
+}
+
+if (!is_android) {
   test("browser_tests") {
     configs += [ "//build/config:precompiled_headers" ]
     defines = [
@@ -604,7 +652,6 @@
       "//mojo/public/cpp/system",
       "//net",
       "//net:test_support",
-      "//net:test_support",
       "//ppapi/buildflags",
       "//printing/buildflags",
       "//rlz/buildflags",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 0c5233f..56417e08 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -73,7 +73,7 @@
     "javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java",
     "javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java",
     "javatests/src/org/chromium/chrome/test/util/browser/suggestions/DummySuggestionsEventReporter.java",
-    "javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeMostVisitedSites.java",
+    "javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java",
     "javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeSuggestionsSource.java",
     "javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java",
     "javatests/src/org/chromium/chrome/test/util/browser/sync/SyncTestUtil.java",
diff --git a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java
index 9b198f9..7ee2d1a 100644
--- a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java
+++ b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java
@@ -23,10 +23,7 @@
         super.onCreate(savedInstanceState);
         appendCommandLineFlags(
                 "--remote-debugging-socket-name android_browsertests_devtools_remote");
-    }
 
-    @Override
-    protected void initializeBrowserProcess() {
         // TODO(danakj): This sets up some of the Chrome Java stuff.
         // AsyncInitializationActivity normally does this for the ChromeActivity.
         // We skip handlePostNativeStartup() for now, which runs child processes.
@@ -37,6 +34,10 @@
         // its ShellManager.
         BrowserParts parts = new EmptyBrowserParts() {};
         ChromeBrowserInitializer.getInstance(getApplicationContext()).handlePreNativeStartup(parts);
+    }
+
+    @Override
+    protected void initializeBrowserProcess() {
         super.initializeBrowserProcess();
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java
index d21bde2..ed3a9d11 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java
@@ -14,9 +14,9 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.suggestions.SiteSuggestion;
-import org.chromium.chrome.browser.suggestions.TileSectionType;
-import org.chromium.chrome.browser.suggestions.TileSource;
-import org.chromium.chrome.browser.suggestions.TileTitleSource;
+import org.chromium.chrome.browser.suggestions.tile.TileSectionType;
+import org.chromium.chrome.browser.suggestions.tile.TileSource;
+import org.chromium.chrome.browser.suggestions.tile.TileTitleSource;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.touchless.TouchlessDelegate;
 import org.chromium.chrome.browser.util.FeatureUtilities;
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeMostVisitedSites.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeMostVisitedSites.java
deleted file mode 100644
index 51587b9..0000000
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeMostVisitedSites.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.test.util.browser.suggestions;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.chrome.browser.suggestions.MostVisitedSites;
-import org.chromium.chrome.browser.suggestions.SiteSuggestion;
-import org.chromium.chrome.browser.suggestions.Tile;
-import org.chromium.chrome.browser.suggestions.TileSectionType;
-import org.chromium.chrome.browser.suggestions.TileSource;
-import org.chromium.chrome.browser.suggestions.TileTitleSource;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-
-/**
- * A fake implementation of MostVisitedSites that returns a fixed list of most visited sites.
- *
- * Once the observer is set (through {@link #setObserver(Observer, int)}), updates to the data must
- * be made on the UI thread, as they can result in UI manipulations.
- */
-public class FakeMostVisitedSites implements MostVisitedSites {
-    private final List<String> mBlacklistedUrls = new ArrayList<>();
-
-    private List<SiteSuggestion> mSites = new ArrayList<>();
-    private Observer mObserver;
-
-    @Override
-    public void destroy() {}
-
-    @Override
-    public void setObserver(Observer observer, int numResults) {
-        mObserver = observer;
-        notifyTileSuggestionsAvailable();
-    }
-
-    @Override
-    public void addBlacklistedUrl(String url) {
-        mBlacklistedUrls.add(url);
-    }
-
-    @Override
-    public void removeBlacklistedUrl(String url) {
-        mBlacklistedUrls.remove(url);
-    }
-
-    @Override
-    public void recordPageImpression(int tilesCount) {
-        // Metrics are stubbed out.
-    }
-
-    @Override
-    public void recordTileImpression(Tile tile) {
-        // Metrics are stubbed out.
-    }
-
-    @Override
-    public void recordOpenedMostVisitedItem(Tile tile) {
-        //  Metrics are stubbed out.
-    }
-
-    /** @return Whether {@link #addBlacklistedUrl} has been called on the given URL. */
-    public boolean isUrlBlacklisted(String url) {
-        return mBlacklistedUrls.contains(url);
-    }
-
-    /**
-     * Sets new tile suggestion data.
-     *
-     * If there is an observer it will be notified and the call has to be made on the UI thread.
-     */
-    public void setTileSuggestions(List<SiteSuggestion> suggestions) {
-        mSites = new ArrayList<>(suggestions);
-        notifyTileSuggestionsAvailable();
-    }
-
-    /**
-     * Sets new tile suggestion data.
-     *
-     * If there is an observer it will be notified and the call has to be made on the UI thread.
-     */
-    public void setTileSuggestions(SiteSuggestion... suggestions) {
-        setTileSuggestions(Arrays.asList(suggestions));
-    }
-
-    /**
-     * Sets new tile suggestion data, generating dummy data for the missing properties.
-     *
-     * If there is an observer it will be notified and the call has to be made on the UI thread.
-     *
-     * @param urls The URLs of the site suggestions.
-     * @see #setTileSuggestions(SiteSuggestion[])
-     */
-    public void setTileSuggestions(String... urls) {
-        setTileSuggestions(createSiteSuggestions(urls));
-    }
-
-    /** @return An unmodifiable view of the current list of sites. */
-    public List<SiteSuggestion> getCurrentSites() {
-        return Collections.unmodifiableList(mSites);
-    }
-
-    public static List<SiteSuggestion> createSiteSuggestions(String... urls) {
-        List<SiteSuggestion> suggestions = new ArrayList<>(urls.length);
-        for (String url : urls) suggestions.add(createSiteSuggestion(url));
-        return suggestions;
-    }
-
-    public static SiteSuggestion createSiteSuggestion(String url) {
-        return createSiteSuggestion(url, url);
-    }
-
-    public static SiteSuggestion createSiteSuggestion(String title, String url) {
-        return new SiteSuggestion(title, url, "", TileTitleSource.TITLE_TAG, TileSource.TOP_SITES,
-                TileSectionType.PERSONALIZED, new Date());
-    }
-
-    private void notifyTileSuggestionsAvailable() {
-        if (mObserver == null) return;
-
-        // Notifying the observer usually results in view modifications, so this call should always
-        // happen on the UI thread. We assert we do it here to make detecting related mistakes in
-        // tests more easily.
-        // To make initialisation easier, we only enforce that once the observer is set, using it as
-        // a signal that the test started and this is not the setup anymore.
-        ThreadUtils.assertOnUiThread();
-
-        mObserver.onSiteSuggestionsAvailable(mSites);
-    }
-}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java
index 7f5a068..7460e3c6 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java
@@ -13,9 +13,9 @@
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.suggestions.MostVisitedSites;
 import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory;
 import org.chromium.chrome.browser.suggestions.SuggestionsEventReporter;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites;
 import org.chromium.chrome.browser.widget.ThumbnailProvider;
 
 /**
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
new file mode 100644
index 0000000..a9cd168
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
@@ -0,0 +1,135 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.util.browser.suggestions.mostvisited;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites;
+import org.chromium.chrome.browser.suggestions.tile.Tile;
+import org.chromium.chrome.browser.suggestions.tile.TileSectionType;
+import org.chromium.chrome.browser.suggestions.tile.TileSource;
+import org.chromium.chrome.browser.suggestions.tile.TileTitleSource;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * A fake implementation of MostVisitedSites that returns a fixed list of most visited sites.
+ *
+ * Once the observer is set (through {@link #setObserver(Observer, int)}), updates to the data must
+ * be made on the UI thread, as they can result in UI manipulations.
+ */
+public class FakeMostVisitedSites implements MostVisitedSites {
+    private final List<String> mBlacklistedUrls = new ArrayList<>();
+
+    private List<SiteSuggestion> mSites = new ArrayList<>();
+    private Observer mObserver;
+
+    @Override
+    public void destroy() {}
+
+    @Override
+    public void setObserver(Observer observer, int numResults) {
+        mObserver = observer;
+        notifyTileSuggestionsAvailable();
+    }
+
+    @Override
+    public void addBlacklistedUrl(String url) {
+        mBlacklistedUrls.add(url);
+    }
+
+    @Override
+    public void removeBlacklistedUrl(String url) {
+        mBlacklistedUrls.remove(url);
+    }
+
+    @Override
+    public void recordPageImpression(int tilesCount) {
+        // Metrics are stubbed out.
+    }
+
+    @Override
+    public void recordTileImpression(Tile tile) {
+        // Metrics are stubbed out.
+    }
+
+    @Override
+    public void recordOpenedMostVisitedItem(Tile tile) {
+        //  Metrics are stubbed out.
+    }
+
+    /** @return Whether {@link #addBlacklistedUrl} has been called on the given URL. */
+    public boolean isUrlBlacklisted(String url) {
+        return mBlacklistedUrls.contains(url);
+    }
+
+    /**
+     * Sets new tile suggestion data.
+     *
+     * If there is an observer it will be notified and the call has to be made on the UI thread.
+     */
+    public void setTileSuggestions(List<SiteSuggestion> suggestions) {
+        mSites = new ArrayList<>(suggestions);
+        notifyTileSuggestionsAvailable();
+    }
+
+    /**
+     * Sets new tile suggestion data.
+     *
+     * If there is an observer it will be notified and the call has to be made on the UI thread.
+     */
+    public void setTileSuggestions(SiteSuggestion... suggestions) {
+        setTileSuggestions(Arrays.asList(suggestions));
+    }
+
+    /**
+     * Sets new tile suggestion data, generating dummy data for the missing properties.
+     *
+     * If there is an observer it will be notified and the call has to be made on the UI thread.
+     *
+     * @param urls The URLs of the site suggestions.
+     * @see #setTileSuggestions(SiteSuggestion[])
+     */
+    public void setTileSuggestions(String... urls) {
+        setTileSuggestions(createSiteSuggestions(urls));
+    }
+
+    /** @return An unmodifiable view of the current list of sites. */
+    public List<SiteSuggestion> getCurrentSites() {
+        return Collections.unmodifiableList(mSites);
+    }
+
+    public static List<SiteSuggestion> createSiteSuggestions(String... urls) {
+        List<SiteSuggestion> suggestions = new ArrayList<>(urls.length);
+        for (String url : urls) suggestions.add(createSiteSuggestion(url));
+        return suggestions;
+    }
+
+    public static SiteSuggestion createSiteSuggestion(String url) {
+        return createSiteSuggestion(url, url);
+    }
+
+    public static SiteSuggestion createSiteSuggestion(String title, String url) {
+        return new SiteSuggestion(title, url, "", TileTitleSource.TITLE_TAG, TileSource.TOP_SITES,
+                TileSectionType.PERSONALIZED, new Date());
+    }
+
+    private void notifyTileSuggestionsAvailable() {
+        if (mObserver == null) return;
+
+        // Notifying the observer usually results in view modifications, so this call should always
+        // happen on the UI thread. We assert we do it here to make detecting related mistakes in
+        // tests more easily.
+        // To make initialisation easier, we only enforce that once the observer is set, using it as
+        // a signal that the test started and this is not the setup anymore.
+        ThreadUtils.assertOnUiThread();
+
+        mObserver.onSiteSuggestionsAvailable(mSites);
+    }
+}
diff --git a/chrome/test/base/android/android_browser_test.cc b/chrome/test/base/android/android_browser_test.cc
new file mode 100644
index 0000000..b7174ac
--- /dev/null
+++ b/chrome/test/base/android/android_browser_test.cc
@@ -0,0 +1,23 @@
+// Copyright 2019 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/test/base/android/android_browser_test.h"
+
+#include "content/public/test/test_utils.h"
+
+AndroidBrowserTest::AndroidBrowserTest() = default;
+AndroidBrowserTest::~AndroidBrowserTest() = default;
+
+void AndroidBrowserTest::PreRunTestOnMainThread() {
+  // Pump startup related events.
+  content::RunAllPendingInMessageLoop();
+}
+
+void AndroidBrowserTest::PostRunTestOnMainThread() {
+  // Sometimes tests leave Quit tasks in the MessageLoop (for shame), so let's
+  // run all pending messages here to avoid preempting the QuitBrowsers tasks.
+  // TODO(https://crbug.com/922118): Remove this once it is no longer possible
+  // to post QuitCurrent* tasks.
+  content::RunAllPendingInMessageLoop();
+}
diff --git a/chrome/test/base/android/android_browser_test.h b/chrome/test/base/android/android_browser_test.h
new file mode 100644
index 0000000..d3c4912a
--- /dev/null
+++ b/chrome/test/base/android/android_browser_test.h
@@ -0,0 +1,20 @@
+// Copyright 2019 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_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
+#define CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
+
+#include "content/public/test/browser_test_base.h"
+
+class AndroidBrowserTest : public content::BrowserTestBase {
+ public:
+  AndroidBrowserTest();
+  ~AndroidBrowserTest() override;
+
+  // content::BrowserTestBase implementation.
+  void PreRunTestOnMainThread() override;
+  void PostRunTestOnMainThread() override;
+};
+
+#endif  // CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
diff --git a/chrome/test/base/android/android_browser_test_browsertest_android.cc b/chrome/test/base/android/android_browser_test_browsertest_android.cc
new file mode 100644
index 0000000..cf2b90f
--- /dev/null
+++ b/chrome/test/base/android/android_browser_test_browsertest_android.cc
@@ -0,0 +1,8 @@
+// Copyright 2019 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/test/base/android/android_browser_test.h"
+#include "content/public/test/browser_test.h"
+
+IN_PROC_BROWSER_TEST_F(AndroidBrowserTest, Smoke) {}
diff --git a/chrome/test/base/web_ui_browser_test.cc b/chrome/test/base/web_ui_browser_test.cc
index 4d711723..5f476f3 100644
--- a/chrome/test/base/web_ui_browser_test.cc
+++ b/chrome/test/base/web_ui_browser_test.cc
@@ -387,7 +387,7 @@
   ~MockWebUIDataSource() override {}
 
  private:
-  std::string GetSource() const override { return "dummyurl"; }
+  std::string GetSource() override { return "dummyurl"; }
 
   void StartDataRequest(
       const std::string& path,
@@ -399,14 +399,14 @@
     callback.Run(response.get());
   }
 
-  std::string GetMimeType(const std::string& path) const override {
+  std::string GetMimeType(const std::string& path) override {
     return "text/html";
   }
 
   // Append 'unsave-eval' to the default script-src CSP policy, since it is
   // needed by some tests using chrome://dummyurl (because they depend on
   // Mock4JS, see crbug.com/844820).
-  std::string GetContentSecurityPolicyScriptSrc() const override {
+  std::string GetContentSecurityPolicyScriptSrc() override {
     return "script-src chrome://resources 'self' 'unsafe-eval';";
   }
 
diff --git a/chrome/test/data/arc_graphics_tracing/trace_async_events.json b/chrome/test/data/arc_graphics_tracing/trace_async_events.json
new file mode 100644
index 0000000..33b6346
--- /dev/null
+++ b/chrome/test/data/arc_graphics_tracing/trace_async_events.json
@@ -0,0 +1,5 @@
+{
+  "traceEvents":[],
+  "systemTraceEvents":"            app1-14141 [001] ...1     1.100000: tracing_mark_write: S|1141|async1|1\n            app2-14142 [001] ...1     1.200000: tracing_mark_write: S|1142|async2|2\n            app1-14141 [001] ...1     1.300000: tracing_mark_write: F|1141|async1|1\n            app2-14142 [001] ...1     1.400000: tracing_mark_write: F|1142|async2|2\n"
+}
+
diff --git a/chrome/test/data/downloads/multiple_a_download_x_origin_redirect_to_download.html b/chrome/test/data/downloads/multiple_a_download_x_origin_redirect_to_download.html
new file mode 100644
index 0000000..6f84610
--- /dev/null
+++ b/chrome/test/data/downloads/multiple_a_download_x_origin_redirect_to_download.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<body></body>
+<script>
+const url_params = new URLSearchParams(location.search);
+let link = document.createElement('a');
+link.download = '';
+link.href = url_params.get('download_url');
+document.body.appendChild(link);
+link.click();
+link.click();
+link.click();
+</script>
\ No newline at end of file
diff --git a/chrome/test/data/downloads/redirect_x_origin_download.html b/chrome/test/data/downloads/redirect_x_origin_download.html
new file mode 100644
index 0000000..c2c06de
--- /dev/null
+++ b/chrome/test/data/downloads/redirect_x_origin_download.html
@@ -0,0 +1 @@
+redirecting
\ No newline at end of file
diff --git a/chrome/test/data/downloads/redirect_x_origin_download.html.mock-http-headers b/chrome/test/data/downloads/redirect_x_origin_download.html.mock-http-headers
new file mode 100644
index 0000000..df083f9
--- /dev/null
+++ b/chrome/test/data/downloads/redirect_x_origin_download.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 302 Moved
+Location: http://www.a.com:{{PORT}}/downloads/empty.bin
\ No newline at end of file
diff --git a/chrome/test/data/webui/print_preview/cloud_print_interface_stub.js b/chrome/test/data/webui/print_preview/cloud_print_interface_stub.js
index bc7b6b9..5b22b48 100644
--- a/chrome/test/data/webui/print_preview/cloud_print_interface_stub.js
+++ b/chrome/test/data/webui/print_preview/cloud_print_interface_stub.js
@@ -43,6 +43,20 @@
     }
 
     /**
+     * Helper method to derive logged in users from the |cloudPrintersMap_|.
+     * @return {!Array<string>} The logged in user accounts.
+     */
+    getUsers_() {
+      const users = [];
+      this.cloudPrintersMap_.forEach((printer, key) => {
+        if (!users.includes(printer.account)) {
+          users.push(printer.account);
+        }
+      });
+      return users;
+    }
+
+    /**
      * Dispatches a CloudPrintInterfaceEventType.SEARCH_DONE event with the
      * printers that have been set so far using setPrinter().
      * @override
@@ -50,6 +64,15 @@
     search(account) {
       this.methodCalled('search', account);
       this.searchInProgress_ = true;
+      const users = this.getUsers_();
+      const activeUser = users.includes(account) ? account : (users[0] || '');
+      if (activeUser) {
+        this.eventTarget_.dispatchEvent(new CustomEvent(
+            cloudprint.CloudPrintInterfaceEventType.UPDATE_USERS,
+            {detail: {users: users, activeUser: activeUser}}));
+        this.initialized_ = true;
+      }
+
       const printers = [];
       this.cloudPrintersMap_.forEach((value) => {
         if (value.account === account) {
@@ -86,12 +109,7 @@
           print_preview.createDestinationKey(printerId, origin, account));
 
       if (!this.initialized_) {
-        const users = [];
-        this.cloudPrintersMap_.forEach((printer, key) => {
-          if (!users.includes(printer.account)) {
-            users.push(printer.account);
-          }
-        });
+        const users = this.getUsers_();
         const activeUser = users.includes(account) ? account : (users[0] || '');
         if (activeUser) {
           this.eventTarget_.dispatchEvent(new CustomEvent(
diff --git a/chrome/test/data/webui/print_preview/destination_select_test.js b/chrome/test/data/webui/print_preview/destination_select_test.js
index 5d91f14..414c4a6 100644
--- a/chrome/test/data/webui/print_preview/destination_select_test.js
+++ b/chrome/test/data/webui/print_preview/destination_select_test.js
@@ -95,7 +95,7 @@
       destinationSettings.init(
           initialSettings.printerName,
           initialSettings.serializedDefaultDestinationSelectionRulesStr,
-          initialSettings.userAccounts);
+          initialSettings.userAccounts, true /* syncAvailable */);
       destinationSettings.disabled = false;
       return opt_expectPrinterFailure ? Promise.resolve() : Promise.race([
         nativeLayer.whenCalled('getPrinterCapabilities'), whenCapabilitiesReady
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js
index 292c945..88d50a1 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.js
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -85,7 +85,7 @@
       destinationSettings.init(
           'FooDevice' /* printerName */,
           '' /* serializedDefaultDestinationSelectionRulesStr */,
-          [] /* userAccounts */);
+          [] /* userAccounts */, true /* syncAvailable */);
       assertTrue(dropdown.hidden);
 
       return test_util
@@ -169,7 +169,7 @@
       destinationSettings.init(
           '' /* printerName */,
           '' /* serializedDefaultDestinationSelectionRulesStr */,
-          initialAccounts);
+          initialAccounts, true /* syncAvailable */);
       destinationSettings.state = print_preview.State.READY;
       destinationSettings.disabled = false;
     }
diff --git a/chrome/test/data/webui/print_preview/user_manager_test.js b/chrome/test/data/webui/print_preview/user_manager_test.js
index 1b3abad..50a0159 100644
--- a/chrome/test/data/webui/print_preview/user_manager_test.js
+++ b/chrome/test/data/webui/print_preview/user_manager_test.js
@@ -32,11 +32,6 @@
       nativeLayer = new print_preview.NativeLayerStub();
       print_preview.NativeLayer.setInstance(nativeLayer);
       cloudPrintInterface = new print_preview.CloudPrintInterfaceStub();
-      // Set up a cloud printer for each account, so that search will work.
-      cloudPrintInterface.setPrinter(
-          print_preview_test_utils.getGoogleDriveDestination(account1));
-      cloudPrintInterface.setPrinter(
-          print_preview_test_utils.getGoogleDriveDestination(account2));
 
       userManager = document.createElement('print-preview-user-manager');
 
@@ -61,13 +56,19 @@
 
     // Checks that initializing and updating user accounts works as expected.
     test('update users', function() {
+      // Set up a cloud printer for each account.
+      cloudPrintInterface.setPrinter(
+          print_preview_test_utils.getGoogleDriveDestination(account1));
+      cloudPrintInterface.setPrinter(
+          print_preview_test_utils.getGoogleDriveDestination(account2));
+
       assertTrue(userManager.cloudPrintDisabled);
 
       userManager.setCloudPrintInterface(cloudPrintInterface);
       assertFalse(userManager.cloudPrintDisabled);
       assertEquals(undefined, userManager.activeUser);
 
-      userManager.initUserAccounts([]);
+      userManager.initUserAccounts([], true /* syncAvailable */);
       assertEquals('', userManager.activeUser);
       assertEquals(0, userManager.users.length);
       assertEquals(0, cloudPrintInterface.getCallCount('search'));
@@ -97,9 +98,61 @@
       assertEquals(4, cloudPrintInterface.getCallCount('search'));
     });
 
-    test('update active user', function() {
+    // Checks that initializing and updating user accounts works as expected
+    // when sync is unavailable.
+    test('update users without sync', function() {
+      assertTrue(userManager.cloudPrintDisabled);
+
       userManager.setCloudPrintInterface(cloudPrintInterface);
-      userManager.initUserAccounts([account1, account2]);
+      assertFalse(userManager.cloudPrintDisabled);
+      assertEquals(undefined, userManager.activeUser);
+
+      userManager.initUserAccounts([], false /* syncAvailable */);
+      return cloudPrintInterface.whenCalled('printer')
+          .then(() => {
+            assertEquals(undefined, userManager.activeUser);
+            assertEquals(0, userManager.users.length);
+            assertEquals(0, cloudPrintInterface.getCallCount('search'));
+            // Need to check for the Google Drive printer by calling
+            // cloudPrintInterface.printer(), since sync is not available.
+            assertEquals(1, cloudPrintInterface.getCallCount('printer'));
+
+            // Simulate signing into an account by setting a cloud printer for
+            // it and firing the 'check-for-account-update' listener.
+            // This should update the list of users and the active user and
+            // trigger a call to search.
+            cloudPrintInterface.setPrinter(
+                print_preview_test_utils.getGoogleDriveDestination(account1));
+            cr.webUIListenerCallback('check-for-account-update');
+            return cloudPrintInterface.whenCalled('search');
+          })
+          .then(() => {
+            assertEquals(account1, userManager.activeUser);
+            assertEquals(1, userManager.users.length);
+            assertEquals(1, cloudPrintInterface.getCallCount('search'));
+
+            // Simulate signing in to a second account.
+            cloudPrintInterface.setPrinter(
+                print_preview_test_utils.getGoogleDriveDestination(account2));
+            cr.webUIListenerCallback('check-for-account-update');
+            return cloudPrintInterface.whenCalled('search');
+          })
+          .then(() => {
+            assertEquals(account1, userManager.activeUser);
+            assertEquals(2, userManager.users.length);
+            assertEquals(2, cloudPrintInterface.getCallCount('search'));
+          });
+    });
+
+    test('update active user', function() {
+      // Set up a cloud printer for each account.
+      cloudPrintInterface.setPrinter(
+          print_preview_test_utils.getGoogleDriveDestination(account1));
+      cloudPrintInterface.setPrinter(
+          print_preview_test_utils.getGoogleDriveDestination(account2));
+      userManager.setCloudPrintInterface(cloudPrintInterface);
+      userManager.initUserAccounts(
+          [account1, account2], true /* syncAvailable */);
       assertFalse(userManager.cloudPrintDisabled);
       assertEquals(account1, userManager.activeUser);
       assertEquals(2, userManager.users.length);
diff --git a/chrome/test/data/webui/signin/.eslintrc.js b/chrome/test/data/webui/signin/.eslintrc.js
new file mode 100644
index 0000000..cce1973
--- /dev/null
+++ b/chrome/test/data/webui/signin/.eslintrc.js
@@ -0,0 +1,9 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module.exports = {
+  'rules': {
+    'no-var': 'error',
+  },
+};
diff --git a/chrome/test/data/webui/signin/signin_browsertest.js b/chrome/test/data/webui/signin/signin_browsertest.js
index 49c483a6..6a6c899 100644
--- a/chrome/test/data/webui/signin/signin_browsertest.js
+++ b/chrome/test/data/webui/signin/signin_browsertest.js
@@ -14,7 +14,8 @@
  * chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.html.
  * This has to be declared as a variable for TEST_F to find it correctly.
  */
-SigninSyncConfirmationTest = class extends PolymerTest {
+// eslint-disable-next-line no-var
+var SigninSyncConfirmationTest = class extends PolymerTest {
   /** @override */
   get typedefCppFixture() {
     return 'SigninBrowserTest';
diff --git a/chrome/test/data/webui/signin/sync_confirmation_test.js b/chrome/test/data/webui/signin/sync_confirmation_test.js
index e0fd1cb..cd766e5 100644
--- a/chrome/test/data/webui/signin/sync_confirmation_test.js
+++ b/chrome/test/data/webui/signin/sync_confirmation_test.js
@@ -9,7 +9,7 @@
     setup(function() {
       PolymerTest.clearBody();
       app = document.createElement('sync-confirmation-app');
-      var accountImageRequested = false;
+      let accountImageRequested = false;
       registerMessageCallback('accountImageRequest', this, function() {
         accountImageRequested = true;
       });
@@ -62,11 +62,8 @@
     // button.
     test('recordConsentOnConfirm', function() {
       app.$$('#confirmButton').click();
-      return browserProxy.whenCalled('confirm').then(function(arguments) {
-        assertEquals(2, arguments.length);
-        var description = arguments[0];
-        var confirmation = arguments[1];
-
+      return browserProxy.whenCalled('confirm').then(function(
+          [description, confirmation]) {
         assertEquals(
             JSON.stringify(STANDARD_CONSENT_DESCRIPTION_TEXT),
             JSON.stringify(description));
@@ -78,11 +75,9 @@
     // button.
     test('recordConsentOnSettingsLink', function() {
       app.$$('#settingsButton').click();
-      return browserProxy.whenCalled('goToSettings').then(function(arguments) {
-        assertEquals(2, arguments.length);
-        var description = arguments[0];
-        var confirmation = arguments[1];
-
+      return browserProxy.whenCalled('goToSettings').then(function([
+        description, confirmation
+      ]) {
         assertEquals(
             JSON.stringify(STANDARD_CONSENT_DESCRIPTION_TEXT),
             JSON.stringify(description));
diff --git a/chromecast/media/base/media_codec_support.cc b/chromecast/media/base/media_codec_support.cc
index ffa7ad1..e50dbfa 100644
--- a/chromecast/media/base/media_codec_support.cc
+++ b/chromecast/media/base/media_codec_support.cc
@@ -50,11 +50,13 @@
     case ::media::kCodecHEVC:
       return kCodecHEVC;
     case ::media::kCodecDolbyVision:
-      if (codec_profile == ::media::DOLBYVISION_PROFILE0) {
+      if (codec_profile == ::media::DOLBYVISION_PROFILE0 ||
+          codec_profile == ::media::DOLBYVISION_PROFILE9) {
         return kCodecDolbyVisionH264;
       } else if (codec_profile == ::media::DOLBYVISION_PROFILE4 ||
                  codec_profile == ::media::DOLBYVISION_PROFILE5 ||
-                 codec_profile == ::media::DOLBYVISION_PROFILE7) {
+                 codec_profile == ::media::DOLBYVISION_PROFILE7 ||
+                 codec_profile == ::media::DOLBYVISION_PROFILE8) {
         return kCodecDolbyVisionHEVC;
       }
       LOG(ERROR) << "Unsupported video codec profile " << codec_profile;
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 123fde0..d15601bc 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12242.0.0
\ No newline at end of file
+12248.0.0
\ No newline at end of file
diff --git a/chromeos/OWNERS b/chromeos/OWNERS
index 7f59483..b1fd1cf1 100644
--- a/chromeos/OWNERS
+++ b/chromeos/OWNERS
@@ -9,7 +9,6 @@
 
 achuith@chromium.org
 alemate@chromium.org
-derat@chromium.org
 oshima@chromium.org
 rkc@chromium.org
 satorux@chromium.org
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index dd487d3..59bbe8d 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -407,6 +407,11 @@
 const char kIgnoreUserProfileMappingForTests[] =
     "ignore-user-profile-mapping-for-tests";
 
+// If set, the Chrome settings will not expose the option to enable crostini
+// unless the enable-experimental-kernel-vm-support flag is set in
+// chrome://flags
+const char kKernelnextRestrictVMs[] = "kernelnext-restrict-vms";
+
 // Enables Chrome-as-a-login-manager behavior.
 const char kLoginManager[] = "login-manager";
 
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 0456610..adb46302 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -155,6 +155,7 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kHomedir[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kIgnoreUserProfileMappingForTests[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kKernelnextRestrictVMs[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginManager[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginProfile[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginUser[];
diff --git a/chromeos/dbus/OWNERS b/chromeos/dbus/OWNERS
index 54cd6ce..c5198cd 100644
--- a/chromeos/dbus/OWNERS
+++ b/chromeos/dbus/OWNERS
@@ -1,6 +1,5 @@
 stevenjb@chromium.org
 hashimoto@chromium.org
-derat@chromium.org
 
 per-file *smb_provider*=amistry@chromium.org
 per-file *smb_provider*=baileyberro@chromium.org
diff --git a/chromeos/dbus/power/OWNERS b/chromeos/dbus/power/OWNERS
deleted file mode 100644
index 3c97e54..0000000
--- a/chromeos/dbus/power/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-derat@chromium.org
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index e6cbe28..7e55d63 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -9,10 +9,8 @@
 #include <string>
 #include <vector>
 
-#include "ash/public/cpp/assistant/default_voice_interaction_observer.h"
 #include "ash/public/interfaces/ash_message_center_controller.mojom.h"
 #include "ash/public/interfaces/assistant_controller.mojom.h"
-#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread.h"
 #include "chromeos/assistant/internal/action/cros_action_module.h"
diff --git a/chromeos/services/cellular_setup/ota_activator.cc b/chromeos/services/cellular_setup/ota_activator.cc
index 9cf5dad..ffc55f7 100644
--- a/chromeos/services/cellular_setup/ota_activator.cc
+++ b/chromeos/services/cellular_setup/ota_activator.cc
@@ -11,13 +11,16 @@
 namespace cellular_setup {
 
 OtaActivator::OtaActivator(base::OnceClosure on_finished_callback)
-    : on_finished_callback_(std::move(on_finished_callback)) {}
+    : on_finished_callback_(std::move(on_finished_callback)), binding_(this) {}
 
 OtaActivator::~OtaActivator() = default;
 
 mojom::CarrierPortalHandlerPtr OtaActivator::GenerateInterfacePtr() {
+  // Only one InterfacePtr should be created per instance.
+  DCHECK(!binding_);
+
   mojom::CarrierPortalHandlerPtr interface_ptr;
-  bindings_.AddBinding(this, mojo::MakeRequest(&interface_ptr));
+  binding_.Bind(mojo::MakeRequest(&interface_ptr));
   return interface_ptr;
 }
 
diff --git a/chromeos/services/cellular_setup/ota_activator.h b/chromeos/services/cellular_setup/ota_activator.h
index 83d3ec3..6846a58 100644
--- a/chromeos/services/cellular_setup/ota_activator.h
+++ b/chromeos/services/cellular_setup/ota_activator.h
@@ -8,7 +8,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "chromeos/services/cellular_setup/public/mojom/cellular_setup.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
 namespace chromeos {
 
@@ -23,6 +23,9 @@
  public:
   ~OtaActivator() override;
 
+  // Generates an InterfacePtr bound to this instance. Only one InterfacePtr may
+  // be bound to a single OtaActivator instance, so this function can only be
+  // called once.
   mojom::CarrierPortalHandlerPtr GenerateInterfacePtr();
 
  protected:
@@ -31,7 +34,7 @@
   void InvokeOnFinishedCallback();
 
   base::OnceClosure on_finished_callback_;
-  mojo::BindingSet<mojom::CarrierPortalHandler> bindings_;
+  mojo::Binding<mojom::CarrierPortalHandler> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(OtaActivator);
 };
diff --git a/components/arc/common/video_accelerator_struct_traits.cc b/components/arc/common/video_accelerator_struct_traits.cc
index dfb8d49..f99d0c7 100644
--- a/components/arc/common/video_accelerator_struct_traits.cc
+++ b/components/arc/common/video_accelerator_struct_traits.cc
@@ -42,12 +42,12 @@
 CHECK_PROFILE_ENUM(HEVCPROFILE_MAIN10);
 CHECK_PROFILE_ENUM(HEVCPROFILE_MAIN_STILL_PICTURE);
 CHECK_PROFILE_ENUM(HEVCPROFILE_MAX);
-CHECK_PROFILE_ENUM(DOLBYVISION_MIN);
 CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE0);
 CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE4);
 CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE5);
 CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE7);
-CHECK_PROFILE_ENUM(DOLBYVISION_MAX);
+CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE8);
+CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE9);
 CHECK_PROFILE_ENUM(THEORAPROFILE_MIN);
 CHECK_PROFILE_ENUM(THEORAPROFILE_ANY);
 CHECK_PROFILE_ENUM(THEORAPROFILE_MAX);
@@ -96,6 +96,8 @@
     case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE4:
     case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE5:
     case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE7:
+    case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE8:
+    case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE9:
     case arc::mojom::VideoCodecProfile::THEORAPROFILE_ANY:
     case arc::mojom::VideoCodecProfile::AV1PROFILE_PROFILE_MAIN:
     case arc::mojom::VideoCodecProfile::AV1PROFILE_PROFILE_HIGH:
diff --git a/components/arc/common/video_common.mojom b/components/arc/common/video_common.mojom
index 188a3509..27e4f86 100644
--- a/components/arc/common/video_common.mojom
+++ b/components/arc/common/video_common.mojom
@@ -41,12 +41,10 @@
   HEVCPROFILE_MAIN10 = 17,
   HEVCPROFILE_MAIN_STILL_PICTURE = 18,
   HEVCPROFILE_MAX = HEVCPROFILE_MAIN_STILL_PICTURE,
-  DOLBYVISION_MIN = 19,
-  DOLBYVISION_PROFILE0 = DOLBYVISION_MIN,
+  DOLBYVISION_PROFILE0 = 19,
   DOLBYVISION_PROFILE4 = 20,
   DOLBYVISION_PROFILE5 = 21,
   DOLBYVISION_PROFILE7 = 22,
-  DOLBYVISION_MAX = DOLBYVISION_PROFILE7,
   THEORAPROFILE_MIN = 23,
   THEORAPROFILE_ANY = THEORAPROFILE_MIN,
   THEORAPROFILE_MAX = THEORAPROFILE_ANY,
@@ -55,7 +53,9 @@
   AV1PROFILE_PROFILE_HIGH = 25,
   AV1PROFILE_PROFILE_PRO = 26,
   AV1PROFILE_MAX = AV1PROFILE_PROFILE_PRO,
-  VIDEO_CODEC_PROFILE_MAX = AV1PROFILE_PROFILE_PRO,
+  DOLBYVISION_PROFILE8 = 27,
+  DOLBYVISION_PROFILE9 = 28,
+  VIDEO_CODEC_PROFILE_MAX = DOLBYVISION_PROFILE9,
 };
 
 [Extensible]
diff --git a/components/autofill/content/browser/risk/fingerprint.cc b/components/autofill/content/browser/risk/fingerprint.cc
index 2e95a90..ab53f72 100644
--- a/components/autofill/content/browser/risk/fingerprint.cc
+++ b/components/autofill/content/browser/risk/fingerprint.cc
@@ -163,7 +163,7 @@
 
 // Writes info about the machine's GPU into the |machine|.
 void AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine,
-                             const content::GpuDataManager& gpu_data_manager) {
+                             content::GpuDataManager& gpu_data_manager) {
   if (!gpu_data_manager.IsEssentialGpuInfoAvailable())
     return;
 
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index a88bf4b..2ac43a0 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -228,7 +228,8 @@
     manager_->OnUserAcceptedCardsFromAccountOption();
   } else {
     if (identifier > 0)  // Denotes an Autofill suggestion.
-      AutofillMetrics::LogAutofillSuggestionAcceptedIndex(position);
+      AutofillMetrics::LogAutofillSuggestionAcceptedIndex(position,
+                                                          popup_type_);
 
     FillAutofillFormData(identifier, false);
   }
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index afa25be6..c9d6c18 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -595,8 +595,7 @@
                                           0);
 }
 
-// Test that an accepted autofill suggestion will fill the form and log the
-// proper metric.
+// Test that an accepted autofill suggestion will fill the form.
 TEST_F(AutofillExternalDelegateUnitTest,
        ExternalDelegateAcceptAutofillSuggestion) {
   EXPECT_CALL(autofill_client_, HideAutofillPopup());
@@ -605,11 +604,9 @@
               FillOrPreviewForm(
                   AutofillDriver::FORM_DATA_ACTION_FILL, _, _, _,
                   kAutofillProfileId));
-  base::HistogramTester histogram;
   external_delegate_->DidAcceptSuggestion(dummy_string,
                                           kAutofillProfileId,
                                           2);  // Row 2
-  histogram.ExpectUniqueSample("Autofill.SuggestionAcceptedIndex", 2, 1);
 }
 
 // Test that the driver is directed to clear the form after being notified that
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc
index a7b9a41..491efbd 100644
--- a/components/autofill/core/browser/autofill_metrics.cc
+++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -1459,10 +1459,23 @@
 }
 
 // static
-void AutofillMetrics::LogAutofillSuggestionAcceptedIndex(int index) {
+void AutofillMetrics::LogAutofillSuggestionAcceptedIndex(int index,
+                                                         PopupType popup_type) {
   base::UmaHistogramSparse("Autofill.SuggestionAcceptedIndex",
                            std::min(index, kMaxBucketsCount));
 
+  if (popup_type == PopupType::kCreditCards) {
+    base::UmaHistogramSparse("Autofill.SuggestionAcceptedIndex.CreditCard",
+                             std::min(index, kMaxBucketsCount));
+  } else if (popup_type == PopupType::kAddresses ||
+             popup_type == PopupType::kPersonalInformation) {
+    base::UmaHistogramSparse("Autofill.SuggestionAcceptedIndex.Profile",
+                             std::min(index, kMaxBucketsCount));
+  } else {
+    base::UmaHistogramSparse("Autofill.SuggestionAcceptedIndex.Other",
+                             std::min(index, kMaxBucketsCount));
+  }
+
   base::RecordAction(base::UserMetricsAction("Autofill_SelectedSuggestion"));
 }
 
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index 5ffab78..425c595 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -20,6 +20,7 @@
 #include "components/autofill/core/browser/form_types.h"
 #include "components/autofill/core/browser/metrics/form_events.h"
 #include "components/autofill/core/browser/sync_utils.h"
+#include "components/autofill/core/browser/ui/popup_types.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/signatures_util.h"
 #include "components/security_state/core/security_state.h"
@@ -1159,7 +1160,8 @@
   static void LogAddressSuggestionsCount(size_t num_suggestions);
 
   // Log the index of the selected Autofill suggestion in the popup.
-  static void LogAutofillSuggestionAcceptedIndex(int index);
+  static void LogAutofillSuggestionAcceptedIndex(int index,
+                                                 PopupType popup_type);
 
   // Logs that the user cleared the form.
   static void LogAutofillFormCleared();
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 7f7c272..584b79b 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -38,6 +38,7 @@
 #include "components/autofill/core/browser/test_form_structure.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/autofill/core/browser/ui/popup_item_ids.h"
+#include "components/autofill/core/browser/ui/popup_types.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -63,6 +64,7 @@
 using base::Bucket;
 using base::TimeTicks;
 using ::testing::ElementsAre;
+using ::testing::HasSubstr;
 using ::testing::Matcher;
 using ::testing::UnorderedPointwise;
 
@@ -8606,6 +8608,59 @@
       /*expected_count=*/1);
 }
 
+TEST_F(AutofillMetricsTest, LogSuggestionAcceptedIndex_CreditCard) {
+  const int index = 2;
+  const PopupType popup_type = PopupType::kCreditCards;
+
+  base::HistogramTester histogram_tester;
+  AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SuggestionAcceptedIndex.CreditCard", index, 1);
+
+  const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(HasSubstr("Autofill.SuggestionAcceptedIndex.Other"),
+                HasSubstr("Autofill.SuggestionAcceptedIndex.Profile"))));
+}
+
+TEST_F(AutofillMetricsTest, LogSuggestionAcceptedIndex_Profile) {
+  const int index = 1;
+  const PopupType popup_type1 = PopupType::kPersonalInformation;
+  const PopupType popup_type2 = PopupType::kAddresses;
+
+  base::HistogramTester histogram_tester;
+  AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type1);
+  AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type2);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.SuggestionAcceptedIndex.Profile", index, 2);
+
+  const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(HasSubstr("Autofill.SuggestionAcceptedIndex.CreditCard"),
+                HasSubstr("Autofill.SuggestionAcceptedIndex.Other"))));
+}
+
+TEST_F(AutofillMetricsTest, LogSuggestionAcceptedIndex_Other) {
+  const int index = 0;
+  const PopupType popup_type1 = PopupType::kUnspecified;
+  const PopupType popup_type2 = PopupType::kPasswords;
+
+  base::HistogramTester histogram_tester;
+  AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type1);
+  AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type2);
+
+  histogram_tester.ExpectUniqueSample("Autofill.SuggestionAcceptedIndex.Other",
+                                      index, 2);
+
+  const std::string histograms = histogram_tester.GetAllHistogramsRecorded();
+  EXPECT_THAT(
+      histograms,
+      Not(AnyOf(HasSubstr("Autofill.SuggestionAcceptedIndex.CreditCard"),
+                HasSubstr("Autofill.SuggestionAcceptedIndex.Profile"))));
+}
+
 TEST_F(AutofillMetricsTest, OnAutocompleteSuggestionsShown) {
   base::HistogramTester histogram_tester;
   AutofillMetrics::OnAutocompleteSuggestionsShown();
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
index 32526960..77043ee4 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
@@ -11,7 +11,6 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
 #include "components/data_reduction_proxy/core/common/uma_util.h"
 #include "net/base/load_flags.h"
 
@@ -49,14 +48,17 @@
 DataReductionProxyURLLoaderThrottle::~DataReductionProxyURLLoaderThrottle() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!private_data_reduction_proxy_info_ && !private_data_reduction_proxy_)
+  if (manager_)
     manager_->RemoveSameSequenceObserver(this);
 }
 
 void DataReductionProxyURLLoaderThrottle::DetachFromCurrentSequence() {
   DETACH_FROM_SEQUENCE(sequence_checker_);
 
-  manager_->RemoveSameSequenceObserver(this);
+  if (manager_) {
+    manager_->RemoveSameSequenceObserver(this);
+    manager_ = nullptr;
+  }
 
   data_reduction_proxy_->Clone(
       mojo::MakeRequest(&private_data_reduction_proxy_info_));
@@ -261,6 +263,14 @@
   }
 }
 
+void DataReductionProxyURLLoaderThrottle::OnThrottleManagerDestroyed(
+    DataReductionProxyThrottleManager* manager) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(manager, manager_);
+  manager_->RemoveSameSequenceObserver(this);
+  manager_ = nullptr;
+}
+
 base::Optional<DataReductionProxyTypeInfo>
 DataReductionProxyURLLoaderThrottle::FindConfiguredDataReductionProxy(
     const net::ProxyServer& proxy_server) const {
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
index 4e83f3fe..fedf821 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
@@ -8,14 +8,13 @@
 #include <vector>
 
 #include "base/sequence_checker.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy.mojom.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
 #include "content/public/common/url_loader_throttle.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
 namespace data_reduction_proxy {
 
-class DataReductionProxyThrottleManager;
 struct DataReductionProxyTypeInfo;
 
 // Handles Data Reduction Proxy logic that needs to be applied to each request.
@@ -27,7 +26,7 @@
 //   * Marking data reduction proxies to be bypassed for future requests.
 class DataReductionProxyURLLoaderThrottle
     : public content::URLLoaderThrottle,
-      public mojom::DataReductionProxyThrottleConfigObserver {
+      public DataReductionProxyThrottleConfigCheckedObserver {
  public:
   // |manager| is shared between all the DRP Throttles.
   DataReductionProxyURLLoaderThrottle(
@@ -55,9 +54,11 @@
   void WillOnCompleteWithError(const network::URLLoaderCompletionStatus& status,
                                bool* defer) override;
 
-  // mojom::DataReductionProxyThrottleConfigObserver:
+  // DataReductionProxyThrottleConfigCheckedObserver:
   void OnThrottleConfigChanged(
       mojom::DataReductionProxyThrottleConfigPtr config) override;
+  void OnThrottleManagerDestroyed(
+      DataReductionProxyThrottleManager* manager) override;
 
  private:
   // As the throttle instance is being moved to another sequence, this
@@ -90,6 +91,9 @@
   std::vector<GURL> url_chain_;
   std::string request_method_;
 
+  // The throttle must be initialized with a valid manager, but can later be
+  // disassociated from it if the manager is destroyed earlier or if the
+  // throttle is moved to a different sequence.
   DataReductionProxyThrottleManager* manager_ = nullptr;
 
   // Throttles that run on the same sequence as the manager share the manager's
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
index d44e544..9e33e7d 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h"
 
 #include "base/run_loop.h"
+#include "base/task/post_task.h"
 #include "base/task/thread_pool/thread_pool.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_task_environment.h"
@@ -141,6 +142,31 @@
   MockMojoDataReductionProxy mock_mojo_data_reduction_proxy_;
 };
 
+TEST_F(DataReductionProxyURLLoaderThrottleTest, ThrottleDiesFirst) {
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy());
+  DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()),
+                                               manager.get());
+}
+
+TEST_F(DataReductionProxyURLLoaderThrottleTest,
+       ThrottleDiesOnDifferentSequence) {
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy());
+  auto throttle = std::make_unique<DataReductionProxyURLLoaderThrottle>(
+      (net::HttpRequestHeaders()), manager.get());
+  throttle->DetachFromCurrentSequence();
+
+  auto task_runner =
+      base::CreateSequencedTaskRunnerWithTraits(base::TaskTraits());
+  task_runner->DeleteSoon(FROM_HERE, throttle.release());
+}
+
+TEST_F(DataReductionProxyURLLoaderThrottleTest, ManagerDiesFirst) {
+  auto manager = CreateManager(mock_mojo_data_reduction_proxy());
+  DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()),
+                                               manager.get());
+  manager.reset();
+}
+
 TEST_F(DataReductionProxyURLLoaderThrottleTest, AcceptTransformHeaderSet) {
   auto manager = CreateManager(mock_mojo_data_reduction_proxy());
   DataReductionProxyURLLoaderThrottle throttle(net::HttpRequestHeaders(),
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc
index 70d8c93..603ab62b1 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc
@@ -30,6 +30,11 @@
 
 DataReductionProxyThrottleManager::~DataReductionProxyThrottleManager() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  for (DataReductionProxyThrottleConfigCheckedObserver& observer :
+       same_sequence_observers_) {
+    observer.OnThrottleManagerDestroyed(this);
+  }
 }
 
 void DataReductionProxyThrottleManager::OnThrottleConfigChanged(
@@ -37,20 +42,20 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   last_proxy_config_ = config.Clone();
 
-  for (mojom::DataReductionProxyThrottleConfigObserver& observer :
+  for (DataReductionProxyThrottleConfigCheckedObserver& observer :
        same_sequence_observers_) {
     observer.OnThrottleConfigChanged(config.Clone());
   }
 }
 
 void DataReductionProxyThrottleManager::AddSameSequenceObserver(
-    mojom::DataReductionProxyThrottleConfigObserver* observer) {
+    DataReductionProxyThrottleConfigCheckedObserver* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   same_sequence_observers_.AddObserver(observer);
 }
 
 void DataReductionProxyThrottleManager::RemoveSameSequenceObserver(
-    mojom::DataReductionProxyThrottleConfigObserver* observer) {
+    DataReductionProxyThrottleConfigCheckedObserver* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   same_sequence_observers_.RemoveObserver(observer);
 }
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h
index 54cabcfd..62a5a354 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h
@@ -13,6 +13,17 @@
 namespace data_reduction_proxy {
 
 class DataReductionProxyServer;
+class DataReductionProxyThrottleManager;
+
+// A throttle config observer that is additionally notified about the manager's
+// destruction.
+class DataReductionProxyThrottleConfigCheckedObserver
+    : public mojom::DataReductionProxyThrottleConfigObserver,
+      public base::CheckedObserver {
+ public:
+  virtual void OnThrottleManagerDestroyed(
+      DataReductionProxyThrottleManager* manager) = 0;
+};
 
 // Helper that encapsulates the shared state between
 // DataReductionProxyURLThrottles, whose main responsibility is keeping the
@@ -36,9 +47,9 @@
   // sign up for / sign out of receiving
   // mojom::DataReductionProxyThrottleConfigObserver events.
   void AddSameSequenceObserver(
-      mojom::DataReductionProxyThrottleConfigObserver* observer);
+      DataReductionProxyThrottleConfigCheckedObserver* observer);
   void RemoveSameSequenceObserver(
-      mojom::DataReductionProxyThrottleConfigObserver* observer);
+      DataReductionProxyThrottleConfigCheckedObserver* observer);
 
   mojom::DataReductionProxy* data_reduction_proxy() {
     return shared_data_reduction_proxy_;
@@ -57,7 +68,8 @@
   // for the interfaces mojom::DataReductionProxy and
   // mojom::DataReductionProxyThrottleConfigObserver.
   mojom::DataReductionProxy* const shared_data_reduction_proxy_;
-  base::ObserverList<mojom::DataReductionProxyThrottleConfigObserver>::Unchecked
+  base::ObserverList<DataReductionProxyThrottleConfigCheckedObserver,
+                     /* check_empty = */ true>
       same_sequence_observers_;
 
   mojo::Binding<
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
index c984483..2ca5e8f4 100644
--- a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
+++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
@@ -230,7 +230,7 @@
 
 DomDistillerViewerSource::~DomDistillerViewerSource() {}
 
-std::string DomDistillerViewerSource::GetSource() const {
+std::string DomDistillerViewerSource::GetSource() {
   return scheme_ + "://";
 }
 
@@ -292,8 +292,7 @@
   callback.Run(base::RefCountedString::TakeString(&unsafe_page_html));
 }
 
-std::string DomDistillerViewerSource::GetMimeType(
-    const std::string& path) const {
+std::string DomDistillerViewerSource::GetMimeType(const std::string& path) {
   if (kViewerCssPath == path)
     return "text/css";
   if (kViewerLoadingImagePath == path)
@@ -304,15 +303,15 @@
 bool DomDistillerViewerSource::ShouldServiceRequest(
     const GURL& url,
     content::ResourceContext* resource_context,
-    int render_process_id) const {
+    int render_process_id) {
   return url.SchemeIs(scheme_);
 }
 
-std::string DomDistillerViewerSource::GetContentSecurityPolicyStyleSrc() const {
+std::string DomDistillerViewerSource::GetContentSecurityPolicyStyleSrc() {
   return "style-src 'self' https://fonts.googleapis.com;";
 }
 
-std::string DomDistillerViewerSource::GetContentSecurityPolicyChildSrc() const {
+std::string DomDistillerViewerSource::GetContentSecurityPolicyChildSrc() {
   return "child-src *;";
 }
 
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source.h b/components/dom_distiller/content/browser/dom_distiller_viewer_source.h
index 6b91b1e..1a285dc5 100644
--- a/components/dom_distiller/content/browser/dom_distiller_viewer_source.h
+++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source.h
@@ -29,17 +29,17 @@
   class RequestViewerHandle;
 
   // Overridden from content::URLDataSource:
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
       const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string& path) const override;
+  std::string GetMimeType(const std::string& path) override;
   bool ShouldServiceRequest(const GURL& url,
                             content::ResourceContext* resource_context,
-                            int render_process_id) const override;
-  std::string GetContentSecurityPolicyStyleSrc() const override;
-  std::string GetContentSecurityPolicyChildSrc() const override;
+                            int render_process_id) override;
+  std::string GetContentSecurityPolicyStyleSrc() override;
+  std::string GetContentSecurityPolicyChildSrc() override;
 
  private:
   friend class DomDistillerViewerSourceTest;
diff --git a/components/feature_engagement/OWNERS b/components/feature_engagement/OWNERS
index f388905..e80cdac 100644
--- a/components/feature_engagement/OWNERS
+++ b/components/feature_engagement/OWNERS
@@ -1,5 +1,12 @@
 dtrainor@chromium.org
 nyquist@chromium.org
 
+per-file *event_constants.*=twellington@chromium.org
+per-file *feature_configurations.cc=twellington@chromium.org
+per-file *feature_constants.*=twellington@chromium.org
+per-file *feature_list.*=twellington@chromium.org
+per-file *EventConstants.java=twellington@chromium.org
+per-file *FeatureConstants.java=twellington@chromium.org
+
 # COMPONENT: Internals>FeatureEngagement
 
diff --git a/components/flags_ui/resources/flags.css b/components/flags_ui/resources/flags.css
index 589c80a..054ee4b 100644
--- a/components/flags_ui/resources/flags.css
+++ b/components/flags_ui/resources/flags.css
@@ -334,6 +334,7 @@
   background: var(--input-background);
   border-radius: 3px;
   box-sizing: border-box;
+  color: inherit;
   font-size: .8125rem;
   margin: 0;
   min-height: 3em;
diff --git a/components/ntp_snippets/remote/json_request_unittest.cc b/components/ntp_snippets/remote/json_request_unittest.cc
index 863b6450..0f084c1 100644
--- a/components/ntp_snippets/remote/json_request_unittest.cc
+++ b/components/ntp_snippets/remote/json_request_unittest.cc
@@ -25,6 +25,13 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+// TODO(crbug.com/961023): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_BuildRequestAuthenticated DISABLED_BuildRequestAuthenticated
+#else
+#define MAYBE_BuildRequestAuthenticated BuildRequestAuthenticated
+#endif
+
 namespace ntp_snippets {
 
 namespace internal {
@@ -110,7 +117,7 @@
   DISALLOW_COPY_AND_ASSIGN(JsonRequestTest);
 };
 
-TEST_F(JsonRequestTest, BuildRequestAuthenticated) {
+TEST_F(JsonRequestTest, MAYBE_BuildRequestAuthenticated) {
   JsonRequest::Builder builder = CreateMinimalBuilder();
   RequestParams params;
   params.excluded_ids = {"1234567890"};
diff --git a/components/ntp_tiles/section_type.h b/components/ntp_tiles/section_type.h
index f1f9f5c..f72a149 100644
--- a/components/ntp_tiles/section_type.h
+++ b/components/ntp_tiles/section_type.h
@@ -10,7 +10,7 @@
 // The type of a section means all its tiles originate here. Ranked descendingly
 // from most important section to least important.
 // A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions.tile
 // GENERATED_JAVA_CLASS_NAME_OVERRIDE: TileSectionType
 enum class SectionType {
   UNKNOWN,
diff --git a/components/ntp_tiles/tile_source.h b/components/ntp_tiles/tile_source.h
index 899e94c..c06f81d1 100644
--- a/components/ntp_tiles/tile_source.h
+++ b/components/ntp_tiles/tile_source.h
@@ -10,7 +10,7 @@
 // The source of an NTP tile. Please update webui/ntp-tiles-internals* as well
 // when modifying these values.
 // A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions.tile
 enum class TileSource {
   // Tile comes from the personal top sites list, based on local history.
   TOP_SITES,
diff --git a/components/ntp_tiles/tile_title_source.h b/components/ntp_tiles/tile_title_source.h
index ed5fdd90..9a2370d 100644
--- a/components/ntp_tiles/tile_title_source.h
+++ b/components/ntp_tiles/tile_title_source.h
@@ -13,7 +13,7 @@
 // enums.xml AND in chrome/browser/resources/local_ntp/most_visited_single.js.
 //
 // A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions.tile
 enum class TileTitleSource {
   // The title might be invalid, aggregated, user-set, extracted from history,
   // not loaded or simply not known.
diff --git a/components/ntp_tiles/tile_visual_type.h b/components/ntp_tiles/tile_visual_type.h
index 6c57f21..7466e46 100644
--- a/components/ntp_tiles/tile_visual_type.h
+++ b/components/ntp_tiles/tile_visual_type.h
@@ -13,7 +13,7 @@
 // histograms/enums.xml.
 //
 // A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions.tile
 enum TileVisualType {
   // The icon or thumbnail hasn't loaded yet.
   NONE = 0,
diff --git a/components/policy/core/common/policy_map.cc b/components/policy/core/common/policy_map.cc
index 119c0c7..7698e3e 100644
--- a/components/policy/core/common/policy_map.cc
+++ b/components/policy/core/common/policy_map.cc
@@ -151,8 +151,7 @@
 bool PolicyMap::Entry::IsBlockedOrIgnored() const {
   return error_message_ids_.find(IDS_POLICY_BLOCKED) !=
              error_message_ids_.end() ||
-         error_message_ids_.find(IDS_POLICY_IGNORED_BY_GROUP_MERGING) !=
-             error_message_ids_.end();
+         IsIgnoredByAtomicGroup();
 }
 
 void PolicyMap::Entry::SetBlocked() {
@@ -163,6 +162,11 @@
   error_message_ids_.insert(IDS_POLICY_IGNORED_BY_GROUP_MERGING);
 }
 
+bool PolicyMap::Entry::IsIgnoredByAtomicGroup() const {
+  return error_message_ids_.find(IDS_POLICY_IGNORED_BY_GROUP_MERGING) !=
+         error_message_ids_.end();
+}
+
 PolicyMap::PolicyMap() {}
 
 PolicyMap::~PolicyMap() {
@@ -232,6 +236,11 @@
   map_[policy].AddError(message_id);
 }
 
+bool PolicyMap::IsPolicyIgnoredByAtomicGroup(const std::string& policy) const {
+  const auto& entry = map_.find(policy);
+  return entry != map_.end() && entry->second.IsIgnoredByAtomicGroup();
+}
+
 void PolicyMap::SetSourceForAll(PolicySource source) {
   for (auto& it : map_) {
     it.second.source = source;
diff --git a/components/policy/core/common/policy_map.h b/components/policy/core/common/policy_map.h
index 69195c5..9d01d45d 100644
--- a/components/policy/core/common/policy_map.h
+++ b/components/policy/core/common/policy_map.h
@@ -88,6 +88,7 @@
     // Marks the policy as ignored because it does not share the priority of
     // its policy atomic group.
     void SetIgnoredByPolicyAtomicGroup();
+    bool IsIgnoredByAtomicGroup() const;
 
     // Callback used to look up a localized string given its l10n message ID. It
     // should return a UTF-16 string.
@@ -146,6 +147,11 @@
   // should only be called for policies that are already stored in the map.
   void AddError(const std::string& policy, int message_id);
 
+  // Return True if the policy is set but its value is ignored because it does
+  // not share the highest priority from its atomic group. Returns False if the
+  // policy is active or not set.
+  bool IsPolicyIgnoredByAtomicGroup(const std::string& policy) const;
+
   // For all policies, overwrite the PolicySource with |source|.
   void SetSourceForAll(PolicySource source);
 
diff --git a/components/policy/core/common/policy_statistics_collector.cc b/components/policy/core/common/policy_statistics_collector.cc
index de3cd16..4c7ce6f 100644
--- a/components/policy/core/common/policy_statistics_collector.cc
+++ b/components/policy/core/common/policy_statistics_collector.cc
@@ -62,6 +62,10 @@
   base::UmaHistogramSparse("Enterprise.Policies", id);
 }
 
+void PolicyStatisticsCollector::RecordPolicyIgnoredByAtomicGroup(int id) {
+  base::UmaHistogramSparse("Enterprise.Policies.IgnoredByPolicyGroup", id);
+}
+
 void PolicyStatisticsCollector::CollectStatistics() {
   const PolicyMap& policies = policy_service_->GetPolicies(
       PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
@@ -76,6 +80,13 @@
       else
         NOTREACHED();
     }
+    if (policies.IsPolicyIgnoredByAtomicGroup(it.key())) {
+      const PolicyDetails* details = get_details_.Run(it.key());
+      if (details)
+        RecordPolicyIgnoredByAtomicGroup(details->id);
+      else
+        NOTREACHED();
+    }
   }
 
   // Take care of next update.
diff --git a/components/policy/core/common/policy_statistics_collector.h b/components/policy/core/common/policy_statistics_collector.h
index f373fd5..2328185 100644
--- a/components/policy/core/common/policy_statistics_collector.h
+++ b/components/policy/core/common/policy_statistics_collector.h
@@ -48,6 +48,9 @@
   // protected virtual for mocking.
   virtual void RecordPolicyUse(int id);
 
+  // protected virtual for mocking.
+  virtual void RecordPolicyIgnoredByAtomicGroup(int id);
+
  private:
   void CollectStatistics();
   void ScheduleUpdate(base::TimeDelta delay);
diff --git a/components/policy/core/common/policy_statistics_collector_unittest.cc b/components/policy/core/common/policy_statistics_collector_unittest.cc
index 80d24980f8..9ab98f3 100644
--- a/components/policy/core/common/policy_statistics_collector_unittest.cc
+++ b/components/policy/core/common/policy_statistics_collector_unittest.cc
@@ -66,6 +66,7 @@
                                   task_runner) {}
 
   MOCK_METHOD1(RecordPolicyUse, void(int));
+  MOCK_METHOD1(RecordPolicyIgnoredByAtomicGroup, void(int));
 };
 
 }  // namespace
@@ -112,6 +113,12 @@
                     nullptr);
   }
 
+  void SetPolicyIgnoredByAtomicGroup(const std::string& name) {
+    SetPolicy(name);
+    auto* policy = policy_map_.GetMutable(name);
+    policy->SetIgnoredByPolicyAtomicGroup();
+  }
+
   base::TimeDelta GetFirstDelay() const {
     if (!task_runner_->HasPendingTask()) {
       ADD_FAILURE();
@@ -186,4 +193,17 @@
   EXPECT_EQ(1u, task_runner_->NumPendingTasks());
 }
 
+TEST_F(PolicyStatisticsCollectorTest, PolicyIgnoredByAtomicGroup) {
+  SetPolicyIgnoredByAtomicGroup(kTestPolicy1);
+
+  prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate,
+                  (base::Time::Now() - update_delay_).ToInternalValue());
+
+  EXPECT_CALL(*policy_statistics_collector_,
+              RecordPolicyIgnoredByAtomicGroup(kTestPolicy1Id));
+
+  policy_statistics_collector_->Initialize();
+  EXPECT_EQ(1u, task_runner_->NumPendingTasks());
+}
+
 }  // namespace policy
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 200119e0e..f96fceb 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -6266,7 +6266,7 @@
           'caption': '''Screen lock delays''',
         },
       ],
-      'supported_on': ['chrome.*:76-'],
+      'supported_on': ['chrome.*:76-', 'chrome_os:76-'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': True,
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc
index e75129c..fdcdf9b 100644
--- a/components/previews/content/previews_decider_impl_unittest.cc
+++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -61,6 +61,19 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+// TODO(crbug.com/961023): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_TestSetBlacklistBoolDueToBlackListState \
+  DISABLED_TestSetBlacklistBoolDueToBlackListState
+#define MAYBE_TestDisallowPreviewBecauseOfBlackListState \
+  DISABLED_TestDisallowPreviewBecauseOfBlackListState
+#else
+#define MAYBE_TestSetBlacklistBoolDueToBlackListState \
+  TestSetBlacklistBoolDueToBlackListState
+#define MAYBE_TestDisallowPreviewBecauseOfBlackListState \
+  TestDisallowPreviewBecauseOfBlackListState
+#endif
+
 namespace previews {
 
 namespace {
@@ -515,7 +528,8 @@
 // Tests most of the reasons that a preview could be disallowed because of the
 // state of the blacklist. Excluded values are USER_RECENTLY_OPTED_OUT,
 // USER_BLACKLISTED, HOST_BLACKLISTED. These are internal to the blacklist.
-TEST_F(PreviewsDeciderImplTest, TestDisallowPreviewBecauseOfBlackListState) {
+TEST_F(PreviewsDeciderImplTest,
+       MAYBE_TestDisallowPreviewBecauseOfBlackListState) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(features::kPreviews);
   base::HistogramTester histogram_tester;
@@ -562,7 +576,7 @@
   variations::testing::ClearAllVariationParams();
 }
 
-TEST_F(PreviewsDeciderImplTest, TestSetBlacklistBoolDueToBlackListState) {
+TEST_F(PreviewsDeciderImplTest, MAYBE_TestSetBlacklistBoolDueToBlackListState) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(features::kPreviews);
 
diff --git a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
index 2b89e65..1ffb647 100644
--- a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
+++ b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
@@ -33,10 +33,10 @@
 
   if (!_inFullScreen) {
     auto* window = base::mac::ObjCCast<NativeWidgetMacNSWindow>([self window]);
-    remote_cocoa::NativeWidgetNSWindowBridge* bridgeImpl = [window bridgeImpl];
-    if (bridgeImpl) {
-      bridgeImpl->host()->GetWindowFrameTitlebarHeight(&overrideTitlebarHeight,
-                                                       &titlebarHeight);
+    remote_cocoa::NativeWidgetNSWindowBridge* bridge = [window bridge];
+    if (bridge) {
+      bridge->host()->GetWindowFrameTitlebarHeight(&overrideTitlebarHeight,
+                                                   &titlebarHeight);
     }
   }
   if (overrideTitlebarHeight)
@@ -91,9 +91,9 @@
 // Keyboard -> Shortcuts -> Keyboard. Usually Ctrl+F5. The argument (|unknown|)
 // tends to just be nil.
 - (void)_handleFocusToolbarHotKey:(id)unknown {
-  remote_cocoa::NativeWidgetNSWindowBridge* bridgeImpl = [self bridgeImpl];
-  if (bridgeImpl)
-    bridgeImpl->host()->OnFocusWindowToolbar();
+  remote_cocoa::NativeWidgetNSWindowBridge* bridge = [self bridge];
+  if (bridge)
+    bridge->host()->OnFocusWindowToolbar();
 }
 
 @end
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
index b71a4d9..e03bbc72 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
@@ -58,8 +58,7 @@
 @property(assign, nonatomic) uint64_t bridgedNativeWidgetId;
 
 // The NativeWidgetNSWindowBridge that this will use to call back to the host.
-@property(assign, nonatomic)
-    remote_cocoa::NativeWidgetNSWindowBridge* bridgeImpl;
+@property(assign, nonatomic) remote_cocoa::NativeWidgetNSWindowBridge* bridge;
 @end
 
 #endif  // COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_NSWINDOW_H_
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
index f74d79b..235b4d50 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -82,10 +82,10 @@
   base::scoped_nsprotocol<id<UserInterfaceItemCommandHandler>> commandHandler_;
   id<WindowTouchBarDelegate> touchBarDelegate_;  // Weak.
   uint64_t bridgedNativeWidgetId_;
-  remote_cocoa::NativeWidgetNSWindowBridge* bridgeImpl_;
+  remote_cocoa::NativeWidgetNSWindowBridge* bridge_;
 }
 @synthesize bridgedNativeWidgetId = bridgedNativeWidgetId_;
-@synthesize bridgeImpl = bridgeImpl_;
+@synthesize bridge = bridge_;
 
 - (instancetype)initWithContentRect:(NSRect)contentRect
                           styleMask:(NSUInteger)windowStyle
@@ -136,14 +136,14 @@
 
 - (BOOL)hasViewsMenuActive {
   bool hasMenuController = false;
-  if (bridgeImpl_)
-    bridgeImpl_->host()->GetHasMenuController(&hasMenuController);
+  if (bridge_)
+    bridge_->host()->GetHasMenuController(&hasMenuController);
   return hasMenuController;
 }
 
 - (id<NSAccessibility>)rootAccessibilityObject {
   id<NSAccessibility> obj =
-      bridgeImpl_ ? bridgeImpl_->host_helper()->GetNativeViewAccessible() : nil;
+      bridge_ ? bridge_->host_helper()->GetNativeViewAccessible() : nil;
   // We should like to DCHECK that the object returned implemements the
   // NSAccessibility protocol, but the NSAccessibilityRemoteUIElement interface
   // does not conform.
@@ -166,8 +166,8 @@
 
 - (BOOL)_isTitleHidden {
   bool shouldShowWindowTitle = YES;
-  if (bridgeImpl_)
-    bridgeImpl_->host()->GetShouldShowWindowTitle(&shouldShowWindowTitle);
+  if (bridge_)
+    bridge_->host()->GetShouldShowWindowTitle(&shouldShowWindowTitle);
   return !shouldShowWindowTitle;
 }
 
@@ -184,22 +184,22 @@
 // down, so check for a delegate.
 - (BOOL)canBecomeKeyWindow {
   bool canBecomeKey = NO;
-  if (bridgeImpl_)
-    bridgeImpl_->host()->GetCanWindowBecomeKey(&canBecomeKey);
+  if (bridge_)
+    bridge_->host()->GetCanWindowBecomeKey(&canBecomeKey);
   return canBecomeKey;
 }
 
 - (BOOL)canBecomeMainWindow {
-  if (!bridgeImpl_)
+  if (!bridge_)
     return NO;
 
   // Dialogs and bubbles shouldn't take large shadows away from their parent.
-  if (bridgeImpl_->parent())
+  if (bridge_->parent())
     return NO;
 
   bool canBecomeKey = NO;
-  if (bridgeImpl_)
-    bridgeImpl_->host()->GetCanWindowBecomeKey(&canBecomeKey);
+  if (bridge_)
+    bridge_->host()->GetCanWindowBecomeKey(&canBecomeKey);
   return canBecomeKey;
 }
 
@@ -211,9 +211,9 @@
   // https://crbug.com/941506.
   if (![NSThread isMainThread])
     return [super hasKeyAppearance];
-  if (bridgeImpl_) {
+  if (bridge_) {
     bool isAlwaysRenderWindowAsKey = NO;
-    bridgeImpl_->host()->GetAlwaysRenderWindowAsKey(&isAlwaysRenderWindowAsKey);
+    bridge_->host()->GetAlwaysRenderWindowAsKey(&isAlwaysRenderWindowAsKey);
     if (isAlwaysRenderWindowAsKey)
       return YES;
   }
@@ -341,10 +341,10 @@
   // properties on the NSWindow and repeats them when focusing an item in the
   // RootView's a11y group. See http://crbug.com/748221.
   id superFocus = [super accessibilityFocusedUIElement];
-  if (!bridgeImpl_ || superFocus != self)
+  if (!bridge_ || superFocus != self)
     return superFocus;
 
-  return bridgeImpl_->host_helper()->GetNativeViewAccessible();
+  return bridge_->host_helper()->GetNativeViewAccessible();
 }
 
 - (NSString*)accessibilityTitle {
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
index 0a12453..af9e024a 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -336,7 +336,7 @@
   window_delegate_.reset(
       [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
   window_ = std::move(window);
-  [window_ setBridgeImpl:this];
+  [window_ setBridge:this];
   [window_ setBridgedNativeWidgetId:id_];
   [window_ setReleasedWhenClosed:NO];  // Owned by scoped_nsobject.
   [window_ setDelegate:window_delegate_];
@@ -831,7 +831,7 @@
   DCHECK(!show_animation_);
 
   [window_ setDelegate:nil];
-  [window_ setBridgeImpl:nullptr];
+  [window_ setBridge:nullptr];
 
   // Ensure that |this| cannot be reached by its id while it is being destroyed.
   size_t erased = GetIdToWidgetImplMap().erase(id_);
diff --git a/components/remote_cocoa/browser/window.mm b/components/remote_cocoa/browser/window.mm
index 071b741..02fc2caa 100644
--- a/components/remote_cocoa/browser/window.mm
+++ b/components/remote_cocoa/browser/window.mm
@@ -14,15 +14,15 @@
 }  // namespace remote_cocoa
 
 @interface NSWindow (Private)
-- (remote_cocoa::NativeWidgetNSWindowBridge*)bridgeImpl;
+- (remote_cocoa::NativeWidgetNSWindowBridge*)bridge;
 @end
 
 namespace remote_cocoa {
 
 bool IsWindowRemote(gfx::NativeWindow gfx_window) {
   NSWindow* ns_window = gfx_window.GetNativeNSWindow();
-  if ([ns_window respondsToSelector:@selector(bridgeImpl)]) {
-    if (![ns_window bridgeImpl])
+  if ([ns_window respondsToSelector:@selector(bridge)]) {
+    if (![ns_window bridge])
       return true;
   }
   return false;
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index 1d2074f..4e932a49 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -654,6 +654,14 @@
   }
 }
 
+void GaiaCookieManagerService::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void GaiaCookieManagerService::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
 void GaiaCookieManagerService::CancelAll() {
   VLOG(1) << "GaiaCookieManagerService::CancelAll";
   gaia_auth_fetcher_.reset();
@@ -687,8 +695,8 @@
 
   if (cause == network::mojom::CookieChangeCause::EXPLICIT) {
     DCHECK(net::CookieChangeCauseIsDeletion(net::CookieChangeCause::EXPLICIT));
-    if (gaia_cookie_deleted_by_user_action_callback_) {
-      gaia_cookie_deleted_by_user_action_callback_.Run();
+    for (auto& observer : observer_list_) {
+      observer.OnGaiaCookieDeletedByUserAction();
     }
   }
 
@@ -736,18 +744,6 @@
   requests_.front().RunSetAccountsInCookieCompletedCallback(result);
 }
 
-void GaiaCookieManagerService::SetGaiaAccountsInCookieUpdatedCallback(
-    GaiaAccountsInCookieUpdatedCallback callback) {
-  DCHECK(!gaia_accounts_updated_in_cookie_callback_);
-  gaia_accounts_updated_in_cookie_callback_ = std::move(callback);
-}
-
-void GaiaCookieManagerService::SetGaiaCookieDeletedByUserActionCallback(
-    GaiaCookieDeletedByUserActionCallback callback) {
-  DCHECK(!gaia_cookie_deleted_by_user_action_callback_);
-  gaia_cookie_deleted_by_user_action_callback_ = std::move(callback);
-}
-
 void GaiaCookieManagerService::OnUbertokenFetchComplete(
     GoogleServiceAuthError error,
     const std::string& uber_token) {
@@ -856,8 +852,8 @@
   // services, in response to OnGaiaAccountsInCookieUpdated, may try in return
   // to call ListAccounts, which would immediately return false if the
   // ListAccounts request is still sitting in queue.
-  if (gaia_accounts_updated_in_cookie_callback_) {
-    gaia_accounts_updated_in_cookie_callback_.Run(
+  for (auto& observer : observer_list_) {
+    observer.OnGaiaAccountsInCookieUpdated(
         listed_accounts_, signed_out_accounts_,
         GoogleServiceAuthError(GoogleServiceAuthError::NONE));
   }
@@ -885,12 +881,10 @@
   }
 
   RecordListAccountsFailure(error.state());
-
-  if (gaia_accounts_updated_in_cookie_callback_) {
-    gaia_accounts_updated_in_cookie_callback_.Run(listed_accounts_,
-                                                  signed_out_accounts_, error);
+  for (auto& observer : observer_list_) {
+    observer.OnGaiaAccountsInCookieUpdated(listed_accounts_,
+                                           signed_out_accounts_, error);
   }
-
   HandleNextRequest();
 }
 
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index 77055d1c..f3fbd22 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -88,12 +88,6 @@
                                   const GoogleServiceAuthError&)>
       AddAccountToCookieCompletedCallback;
 
-  typedef base::RepeatingCallback<void(const std::vector<gaia::ListedAccount>&,
-                                       const std::vector<gaia::ListedAccount>&,
-                                       const GoogleServiceAuthError&)>
-      GaiaAccountsInCookieUpdatedCallback;
-  typedef base::RepeatingCallback<void()> GaiaCookieDeletedByUserActionCallback;
-
   // Contains the information and parameters for any request.
   class GaiaCookieRequest {
    public:
@@ -154,6 +148,28 @@
     DISALLOW_COPY_AND_ASSIGN(GaiaCookieRequest);
   };
 
+  class Observer {
+   public:
+    // Called whenever the GaiaCookieManagerService's list of GAIA accounts is
+    // updated. The GCMS monitors the APISID cookie and triggers a /ListAccounts
+    // call on change. The GCMS will also call ListAccounts upon the first call
+    // to ListAccounts(). The GCMS will delay calling ListAccounts if other
+    // requests are in queue that would modify the APISID cookie.
+    // If the ListAccounts call fails and the GCMS cannot recover, the reason
+    // is passed in |error|.
+    virtual void OnGaiaAccountsInCookieUpdated(
+        const std::vector<gaia::ListedAccount>& accounts,
+        const std::vector<gaia::ListedAccount>& signed_out_accounts,
+        const GoogleServiceAuthError& error) {}
+
+    // Called when the Gaia cookie has been deleted explicitly by a user action,
+    // e.g. from the settings or by an extension.
+    virtual void OnGaiaCookieDeletedByUserAction() {}
+
+   protected:
+    virtual ~Observer() {}
+  };
+
   // Class to retrieve the external connection check results from gaia.
   // Declared publicly for unit tests.
   class ExternalCcResultFetcher : public GaiaAuthConsumer {
@@ -258,6 +274,10 @@
   // service. Virtual for testing.
   virtual void ForceOnCookieChangeProcessing();
 
+  // Add or remove observers of this helper.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   // Cancel all login requests.
   void CancelAll();
 
@@ -279,25 +299,6 @@
     list_accounts_stale_ = stale;
   }
 
-  // If set, this callback will be invoked whenever the
-  // GaiaCookieManagerService's list of GAIA accounts is updated. The GCMS
-  // monitors the APISID cookie and triggers a /ListAccounts call on change.
-  // The GCMS will also call ListAccounts upon the first call to
-  // ListAccounts(). The GCMS will delay calling ListAccounts if other
-  // requests are in queue that would modify the APISID cookie.
-  // If the ListAccounts call fails and the GCMS cannot recover, the reason
-  // is passed in |error|.
-  // This method can only be called once.
-  void SetGaiaAccountsInCookieUpdatedCallback(
-      GaiaAccountsInCookieUpdatedCallback callback);
-
-  // If set, this callback will be invoked whenever the Gaia cookie has
-  // been deleted explicitly by a user action, e.g. from the settings or by an
-  // extension.
-  // This method can only be called once.
-  void SetGaiaCookieDeletedByUserActionCallback(
-      GaiaCookieDeletedByUserActionCallback callback);
-
   // Returns a non-NULL pointer to its instance of net::BackoffEntry
   const net::BackoffEntry* GetBackoffEntry() { return &fetcher_backoff_; }
 
@@ -364,11 +365,6 @@
   OAuth2TokenService* token_service_;
   SigninClient* signin_client_;
 
-  GaiaAccountsInCookieUpdatedCallback gaia_accounts_updated_in_cookie_callback_;
-  GaiaCookieDeletedByUserActionCallback
-      gaia_cookie_deleted_by_user_action_callback_;
-  base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()>
-      shared_url_loader_factory_getter_;
   std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
   std::unique_ptr<signin::UbertokenFetcherImpl> uber_token_fetcher_;
   ExternalCcResultFetcher external_cc_result_fetcher_;
@@ -394,6 +390,10 @@
   // executed at a time.
   base::circular_deque<GaiaCookieRequest> requests_;
 
+  // List of observers to notify when merge session completes.
+  // Makes sure list is empty on destruction.
+  base::ObserverList<Observer, true>::Unchecked observer_list_;
+
   // True once the ExternalCCResultFetcher has completed once.
   bool external_cc_result_fetched_;
 
diff --git a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
index 5a27c8f..80c2108c 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
@@ -38,19 +38,21 @@
 using MockAddAccountToCookieCompletedCallback = base::MockCallback<
     GaiaCookieManagerService::AddAccountToCookieCompletedCallback>;
 
-class MockObserver {
+class MockObserver : public GaiaCookieManagerService::Observer {
  public:
-  explicit MockObserver(GaiaCookieManagerService* helper) {
-    helper->SetGaiaAccountsInCookieUpdatedCallback(base::BindRepeating(
-        &MockObserver::OnGaiaAccountsInCookieUpdated, base::Unretained(this)));
+  explicit MockObserver(GaiaCookieManagerService* helper) : helper_(helper) {
+    helper_->AddObserver(this);
   }
 
+  ~MockObserver() override { helper_->RemoveObserver(this); }
+
   MOCK_METHOD3(OnGaiaAccountsInCookieUpdated,
                void(const std::vector<gaia::ListedAccount>&,
                     const std::vector<gaia::ListedAccount>&,
                     const GoogleServiceAuthError&));
 
  private:
+  GaiaCookieManagerService* helper_;
 
   DISALLOW_COPY_AND_ASSIGN(MockObserver);
 };
diff --git a/components/signin/core/browser/identity_manager_wrapper.cc b/components/signin/core/browser/identity_manager_wrapper.cc
index fab2d43..2025ebc 100644
--- a/components/signin/core/browser/identity_manager_wrapper.cc
+++ b/components/signin/core/browser/identity_manager_wrapper.cc
@@ -5,7 +5,6 @@
 #include "components/signin/core/browser/identity_manager_wrapper.h"
 
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "services/identity/public/cpp/accounts_cookie_mutator.h"
 #include "services/identity/public/cpp/accounts_mutator.h"
 #include "services/identity/public/cpp/diagnostics_provider.h"
diff --git a/components/sync/driver/sync_session_durations_metrics_recorder.h b/components/sync/driver/sync_session_durations_metrics_recorder.h
index b69ed49..f4860817 100644
--- a/components/sync/driver/sync_session_durations_metrics_recorder.h
+++ b/components/sync/driver/sync_session_durations_metrics_recorder.h
@@ -20,7 +20,8 @@
 // Tracks the active browsing time that the user spends signed in and/or syncing
 // as fraction of their total browsing time.
 class SyncSessionDurationsMetricsRecorder
-    : public syncer::SyncServiceObserver,
+    : public GaiaCookieManagerService::Observer,
+      public syncer::SyncServiceObserver,
       public identity::IdentityManager::Observer {
  public:
   // Callers must ensure that the parameters outlive this object.
diff --git a/components/timers/OWNERS b/components/timers/OWNERS
index 0c43483..8a33102 100644
--- a/components/timers/OWNERS
+++ b/components/timers/OWNERS
@@ -1,2 +1 @@
 chirantan@chromium.org
-derat@chromium.org
diff --git a/components/ui_devtools/BUILD.gn b/components/ui_devtools/BUILD.gn
index a0e8576..1f221f2 100644
--- a/components/ui_devtools/BUILD.gn
+++ b/components/ui_devtools/BUILD.gn
@@ -88,11 +88,11 @@
   }
 
   deps = [
+    ":devtools_protocol_encoding",
     ":protocol_generated_sources",
     "//base",
     "//net",
     "//ui/gfx",
-    "//third_party/inspector_protocol:encoding",
   ]
 
   public_deps = [
@@ -108,11 +108,23 @@
     "ui_devtools_unittest_utils.h",
   ]
   public_deps = [
+    ":devtools_protocol_encoding",
     ":ui_devtools",
     "//testing/gmock",
   ]
 }
 
+static_library("devtools_protocol_encoding") {
+  sources = [
+    "devtools_protocol_encoding.cc",
+    "devtools_protocol_encoding.h",
+  ]
+  deps = [
+    "//base",
+    "//third_party/inspector_protocol:encoding",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
 
diff --git a/components/ui_devtools/DEPS b/components/ui_devtools/DEPS
index 73b01ba..9dab257 100644
--- a/components/ui_devtools/DEPS
+++ b/components/ui_devtools/DEPS
@@ -4,6 +4,7 @@
   "+services/network/public/cpp",
   "+services/network/public/mojom",
   "+third_party/blink/renderer/platform/inspector_protocol",
+  "+third_party/inspector_protocol",
   "+ui/gfx",
 ]
 
diff --git a/components/ui_devtools/devtools_client.cc b/components/ui_devtools/devtools_client.cc
index 865f209b1..e84fdace 100644
--- a/components/ui_devtools/devtools_client.cc
+++ b/components/ui_devtools/devtools_client.cc
@@ -4,6 +4,7 @@
 
 #include "components/ui_devtools/devtools_client.h"
 
+#include "components/ui_devtools/devtools_protocol_encoding.h"
 #include "components/ui_devtools/devtools_server.h"
 
 namespace ui_devtools {
@@ -56,17 +57,32 @@
     agent->Disable();
 }
 
+namespace {
+std::string SerializeToJSON(std::unique_ptr<protocol::Serializable> message) {
+  std::vector<uint8_t> cbor = message->serializeToBinary();
+  std::string json;
+  ::inspector_protocol_encoding::Status status =
+      ConvertCBORToJSON(::inspector_protocol_encoding::SpanFrom(cbor), &json);
+  LOG_IF(ERROR, !status.ok()) << status.ToASCIIString();
+  return json;
+}
+}  // namespace
+
 void UiDevToolsClient::sendProtocolResponse(
     int callId,
     std::unique_ptr<protocol::Serializable> message) {
-  if (connected())
-    server_->SendOverWebSocket(connection_id_, message->serialize(false));
+  if (connected()) {
+    server_->SendOverWebSocket(
+        connection_id_, base::StringPiece(SerializeToJSON(std::move(message))));
+  }
 }
 
 void UiDevToolsClient::sendProtocolNotification(
     std::unique_ptr<protocol::Serializable> message) {
-  if (connected())
-    server_->SendOverWebSocket(connection_id_, message->serialize(false));
+  if (connected()) {
+    server_->SendOverWebSocket(
+        connection_id_, base::StringPiece(SerializeToJSON(std::move(message))));
+  }
 }
 
 void UiDevToolsClient::flushProtocolNotifications() {
diff --git a/components/ui_devtools/devtools_protocol_encoding.cc b/components/ui_devtools/devtools_protocol_encoding.cc
new file mode 100644
index 0000000..760ca2a
--- /dev/null
+++ b/components/ui_devtools/devtools_protocol_encoding.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 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/ui_devtools/devtools_protocol_encoding.h"
+
+#include <memory>
+#include "base/strings/string_number_conversions.h"
+
+namespace ui_devtools {
+namespace {
+using ::inspector_protocol_encoding::span;
+using IPEStatus = ::inspector_protocol_encoding::Status;
+
+// Platform allows us to inject the string<->double conversion
+// routines from base:: into the inspector_protocol JSON parser / serializer.
+class Platform : public ::inspector_protocol_encoding::json::Platform {
+ public:
+  bool StrToD(const char* str, double* result) const override {
+    return base::StringToDouble(str, result);
+  }
+
+  // Prints |value| in a format suitable for JSON.
+  std::unique_ptr<char[]> DToStr(double value) const override {
+    std::string str = base::NumberToString(value);
+    std::unique_ptr<char[]> result(new char[str.size() + 1]);
+    memcpy(result.get(), str.c_str(), str.size() + 1);
+    return result;
+  }
+};
+}  // namespace
+
+IPEStatus ConvertCBORToJSON(span<uint8_t> cbor, std::string* json) {
+  Platform platform;
+  return ::inspector_protocol_encoding::json::ConvertCBORToJSON(platform, cbor,
+                                                                json);
+}
+}  // namespace ui_devtools
diff --git a/components/ui_devtools/devtools_protocol_encoding.h b/components/ui_devtools/devtools_protocol_encoding.h
new file mode 100644
index 0000000..caf57ed9
--- /dev/null
+++ b/components/ui_devtools/devtools_protocol_encoding.h
@@ -0,0 +1,21 @@
+// Copyright 2019 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_UI_DEVTOOLS_DEVTOOLS_PROTOCOL_ENCODING_H_
+#define COMPONENTS_UI_DEVTOOLS_DEVTOOLS_PROTOCOL_ENCODING_H_
+
+#include "third_party/inspector_protocol/encoding/encoding.h"
+
+// Convenience adaptation of the conversion function
+// ::inspector_protocol_encoding::json::ConvertCBORToJSON,
+// using an implementation of
+// ::inspector_protocol_encoding::json::Platform that
+// delegates to base/strings/string_number_conversions.h.
+namespace ui_devtools {
+::inspector_protocol_encoding::Status ConvertCBORToJSON(
+    ::inspector_protocol_encoding::span<uint8_t> cbor,
+    std::string* json);
+}  // namespace ui_devtools
+
+#endif  // COMPONENTS_UI_DEVTOOLS_DEVTOOLS_PROTOCOL_ENCODING_H_
diff --git a/components/ui_devtools/devtools_server.cc b/components/ui_devtools/devtools_server.cc
index 028697c..a5f2cdd 100644
--- a/components/ui_devtools/devtools_server.cc
+++ b/components/ui_devtools/devtools_server.cc
@@ -168,7 +168,7 @@
 }
 
 void UiDevToolsServer::SendOverWebSocket(int connection_id,
-                                         const protocol::String& message) {
+                                         base::StringPiece message) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(devtools_server_sequence_);
   server_->SendOverWebSocket(connection_id, message, tag_);
 }
diff --git a/components/ui_devtools/devtools_server.h b/components/ui_devtools/devtools_server.h
index 60ff011..33f00552 100644
--- a/components/ui_devtools/devtools_server.h
+++ b/components/ui_devtools/devtools_server.h
@@ -10,6 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/threading/thread.h"
 #include "components/ui_devtools/DOM.h"
 #include "components/ui_devtools/Forward.h"
@@ -62,7 +63,7 @@
                                int default_port);
 
   void AttachClient(std::unique_ptr<UiDevToolsClient> client);
-  void SendOverWebSocket(int connection_id, const protocol::String& message);
+  void SendOverWebSocket(int connection_id, base::StringPiece message);
 
   int port() const { return port_; }
 
diff --git a/components/ui_devtools/ui_devtools_unittest_utils.cc b/components/ui_devtools/ui_devtools_unittest_utils.cc
index a2303d5e..f7b80a4 100644
--- a/components/ui_devtools/ui_devtools_unittest_utils.cc
+++ b/components/ui_devtools/ui_devtools_unittest_utils.cc
@@ -5,6 +5,7 @@
 #include "components/ui_devtools/ui_devtools_unittest_utils.h"
 
 #include "base/strings/string_util.h"
+#include "components/ui_devtools/devtools_protocol_encoding.h"
 
 namespace ui_devtools {
 
@@ -29,10 +30,22 @@
                     protocol_notification_messages_.end(), message);
 }
 
+namespace {
+std::string SerializeToJSON(std::unique_ptr<protocol::Serializable> message) {
+  std::vector<uint8_t> cbor = message->serializeToBinary();
+  std::string json;
+  ::inspector_protocol_encoding::Status status =
+      ConvertCBORToJSON(::inspector_protocol_encoding::SpanFrom(cbor), &json);
+  DCHECK(status.ok()) << status.ToASCIIString();
+  return json;
+}
+}  // namespace
+
 void FakeFrontendChannel::sendProtocolNotification(
     std::unique_ptr<protocol::Serializable> message) {
   EXPECT_TRUE(allow_notifications_);
-  protocol_notification_messages_.push_back(message->serialize(false));
+  protocol_notification_messages_.push_back(
+      SerializeToJSON(std::move(message)));
 }
 
 }  // namespace ui_devtools
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index b57e7ce6..40ca7db 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -3396,52 +3396,37 @@
   support_->SetNeedsBeginFrame(true);
 
   // Helper fn to submit a CF.
-  auto submit_frame = [this](RenderPassList* pass_list) {
+  auto submit_frame = [this]() {
+    RenderPassList pass_list;
     auto pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 100, 100);
     pass->damage_rect = gfx::Rect(10, 10, 1, 1);
     pass->id = 1u;
-    pass_list->push_back(std::move(pass));
+    pass_list.push_back(std::move(pass));
 
     SubmitCompositorFrame(
-        pass_list,
+        &pass_list,
         id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id());
   };
 
-  // BeginFrame should not be throttled when the client has not submitted any
-  // compositor frames.
-  base::TimeTicks frame_time = base::TimeTicks::Now();
-  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
-  UpdateBeginFrameTime(support_.get(), frame_time);
-
-  // Submit the first frame for the client. Begin-frame should still not be
-  // throttled since it has not been embedded yet.
-  RenderPassList pass_list;
-  submit_frame(&pass_list);
+  // Submit kUndrawnFrameLimit+1 frames. BeginFrames should be throttled only
+  // after the last frame.
+  base::TimeTicks frame_time;
+  for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1;
+       ++i) {
+    frame_time = base::TimeTicks::Now();
+    EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
+    UpdateBeginFrameTime(support_.get(), frame_time);
+    submit_frame();
+    // Immediately after submitting frame, because there is presentation
+    // feedback queued up, ShouldSendBeginFrame should always return true.
+    EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
+    // Clear the presentation feedbacks.
+    UpdateBeginFrameTime(support_.get(), frame_time);
+  }
   frame_time = base::TimeTicks::Now();
-  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
-  UpdateBeginFrameTime(support_.get(), frame_time);
-
-  display_->DrawAndSwap();
-  frame_time = base::TimeTicks::Now();
-  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
-  UpdateBeginFrameTime(support_.get(), frame_time);
-
-  // Submit a second frame. This frame should not be throttled, even after
-  // presentation-feedbacks, as we allow up to two undrawn frames.
-  submit_frame(&pass_list);
-  frame_time = base::TimeTicks::Now();
-  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
-  UpdateBeginFrameTime(support_.get(), frame_time);
-  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
-
-  // Submit a third frame. This frame should be throttled after
-  // presentation-feedbacks, as we throttle at two undrawn frames.
-  submit_frame(&pass_list);
-  frame_time = base::TimeTicks::Now();
-  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
-  UpdateBeginFrameTime(support_.get(), frame_time);
   EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time));
+  UpdateBeginFrameTime(support_.get(), frame_time);
 
   // Drawing should unthrottle begin-frames.
   display_->DrawAndSwap();
@@ -3449,17 +3434,24 @@
   EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
   UpdateBeginFrameTime(support_.get(), frame_time);
 
-  // Submit two more frames. Begin-frame should be throttled after the
-  // begin-frame for presenatation-feedback.
-  submit_frame(&pass_list);
+  // Verify that throttling starts again after kUndrawnFrameLimit+1 frames.
+  for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1;
+       ++i) {
+    // This clears the presentation feedbacks.
+    UpdateBeginFrameTime(support_.get(), frame_time);
+    frame_time = base::TimeTicks::Now();
+    EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
+    UpdateBeginFrameTime(support_.get(), frame_time);
+    submit_frame();
+    // Immediately after submitting frame, because there is presentation
+    // feedback queued up, ShouldSendBeginFrame should always return true.
+    EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
+    // Clear the presentation feedbacks.
+    UpdateBeginFrameTime(support_.get(), frame_time);
+  }
   frame_time = base::TimeTicks::Now();
-  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
-  UpdateBeginFrameTime(support_.get(), frame_time);
-  submit_frame(&pass_list);
-  frame_time = base::TimeTicks::Now();
-  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
-  UpdateBeginFrameTime(support_.get(), frame_time);
   EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time));
+  UpdateBeginFrameTime(support_.get(), frame_time);
 
   // Instead of doing a draw, forward time by ~1 seconds. That should unthrottle
   // the begin-frame.
@@ -3469,6 +3461,169 @@
   TearDownDisplay();
 }
 
+TEST_F(DisplayTest, BeginFrameThrottlingMultipleSurfaces) {
+  id_allocator_.GenerateId();
+  SetUpGpuDisplay(RendererSettings());
+
+  StubDisplayClient client;
+  display_->Initialize(&client, manager_.surface_manager());
+  display_->SetLocalSurfaceId(
+      id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(),
+      1.f);
+  support_->SetNeedsBeginFrame(true);
+
+  // Helper fn to submit a CF.
+  auto submit_frame = [this]() {
+    RenderPassList pass_list;
+    auto pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+    pass->id = 1u;
+    pass_list.push_back(std::move(pass));
+
+    SubmitCompositorFrame(
+        &pass_list,
+        id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id());
+  };
+
+  // Submit kUndrawnFrameLimit frames. BeginFrames should be throttled only
+  // after the last frame.
+  base::TimeTicks frame_time;
+  for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1;
+       ++i) {
+    frame_time = base::TimeTicks::Now();
+    EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
+    UpdateBeginFrameTime(support_.get(), frame_time);
+    submit_frame();
+    // Generate a new LocalSurfaceId for the next submission.
+    id_allocator_.GenerateId();
+  }
+  frame_time = base::TimeTicks::Now();
+  EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time));
+  UpdateBeginFrameTime(support_.get(), frame_time);
+
+  // This only draws the first surface, so we should only be able to send one
+  // more BeginFrame.
+  display_->DrawAndSwap();
+  frame_time = base::TimeTicks::Now();
+  EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
+  UpdateBeginFrameTime(support_.get(), frame_time);
+
+  // After this frame submission, we are throttled again.
+  submit_frame();
+  frame_time = base::TimeTicks::Now();
+  EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time));
+  UpdateBeginFrameTime(support_.get(), frame_time);
+
+  // Now the last surface is drawn. This should unblock us to submit
+  // kUndrawnFrameLimit+1 frames again.
+  display_->SetLocalSurfaceId(
+      id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(),
+      1.f);
+  display_->DrawAndSwap();
+  id_allocator_.GenerateId();
+  for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1;
+       ++i) {
+    frame_time = base::TimeTicks::Now();
+    EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time));
+    UpdateBeginFrameTime(support_.get(), frame_time);
+    submit_frame();
+    // Generate a new LocalSurfaceId for the next submission.
+    id_allocator_.GenerateId();
+  }
+  frame_time = base::TimeTicks::Now();
+  EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time));
+  UpdateBeginFrameTime(support_.get(), frame_time);
+
+  TearDownDisplay();
+}
+
+TEST_F(DisplayTest, DontThrottleWhenParentBlocked) {
+  id_allocator_.GenerateId();
+  SetUpGpuDisplay(RendererSettings());
+
+  StubDisplayClient client;
+  display_->Initialize(&client, manager_.surface_manager());
+  display_->SetLocalSurfaceId(
+      id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(),
+      1.f);
+  support_->SetNeedsBeginFrame(true);
+
+  // Create frame sink for a sub surface.
+  const LocalSurfaceId sub_local_surface_id(6,
+                                            base::UnguessableToken::Create());
+  const LocalSurfaceId sub_local_surface_id2(7,
+                                             base::UnguessableToken::Create());
+  const SurfaceId sub_surface_id2(kAnotherFrameSinkId, sub_local_surface_id2);
+
+  MockCompositorFrameSinkClient sub_client;
+
+  auto sub_support = std::make_unique<CompositorFrameSinkSupport>(
+      &sub_client, &manager_, kAnotherFrameSinkId, false /* is_root */,
+      true /* needs_sync_points */);
+  sub_support->SetNeedsBeginFrame(true);
+
+  // Submit kUndrawnFrameLimit+1 frames. BeginFrames should be throttled only
+  // after the last frame.
+  base::TimeTicks frame_time;
+  for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1;
+       ++i) {
+    frame_time = base::TimeTicks::Now();
+    EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time));
+    UpdateBeginFrameTime(sub_support.get(), frame_time);
+    sub_support->SubmitCompositorFrame(sub_local_surface_id,
+                                       MakeDefaultCompositorFrame());
+    // Immediately after submitting frame, because there is presentation
+    // feedback queued up, ShouldSendBeginFrame should always return true.
+    EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time));
+    // Clear the presentation feedbacks.
+    UpdateBeginFrameTime(sub_support.get(), frame_time);
+  }
+  frame_time = base::TimeTicks::Now();
+  EXPECT_FALSE(ShouldSendBeginFrame(sub_support.get(), frame_time));
+  UpdateBeginFrameTime(sub_support.get(), frame_time);
+
+  // Make the display block on |sub_local_surface_id2|.
+  CompositorFrame frame =
+      CompositorFrameBuilder()
+          .AddDefaultRenderPass()
+          .SetActivationDependencies({sub_surface_id2})
+          .SetDeadline(FrameDeadline(base::TimeTicks::Now(),
+                                     std::numeric_limits<uint32_t>::max(),
+                                     base::TimeDelta::FromSeconds(1), false))
+          .Build();
+  support_->SubmitCompositorFrame(
+      id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(),
+      std::move(frame));
+
+  for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit * 3;
+       ++i) {
+    frame_time = base::TimeTicks::Now();
+    EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time));
+    UpdateBeginFrameTime(sub_support.get(), frame_time);
+    sub_support->SubmitCompositorFrame(sub_local_surface_id,
+                                       MakeDefaultCompositorFrame());
+    // Immediately after submitting frame, because there is presentation
+    // feedback queued up, ShouldSendBeginFrame should always return true.
+    EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time));
+    // Clear the presentation feedbacks.
+    UpdateBeginFrameTime(sub_support.get(), frame_time);
+  }
+
+  // Now submit to |sub_local_surface_id2|. This should unblock the parent and
+  // throttling will resume.
+  frame_time = base::TimeTicks::Now();
+  EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time));
+  UpdateBeginFrameTime(sub_support.get(), frame_time);
+  sub_support->SubmitCompositorFrame(sub_local_surface_id2,
+                                     MakeDefaultCompositorFrame());
+  frame_time = base::TimeTicks::Now();
+  EXPECT_FALSE(ShouldSendBeginFrame(sub_support.get(), frame_time));
+  UpdateBeginFrameTime(sub_support.get(), frame_time);
+
+  TearDownDisplay();
+}
+
 TEST_F(DisplayTest, InvalidPresentationTimestamps) {
   RendererSettings settings;
   id_allocator_.GenerateId();
diff --git a/components/viz/service/display/frame_rate_decider_unittest.cc b/components/viz/service/display/frame_rate_decider_unittest.cc
index d2502fbd..d80c066 100644
--- a/components/viz/service/display/frame_rate_decider_unittest.cc
+++ b/components/viz/service/display/frame_rate_decider_unittest.cc
@@ -65,7 +65,7 @@
     SurfaceInfo surface_info(surface_id, frame_.device_scale_factor(),
                              frame_.size_in_pixels());
     auto* surface =
-        surface_manager_->CreateSurface(surface_client(), surface_info, false);
+        surface_manager_->CreateSurface(surface_client(), surface_info);
 
     {
       FrameRateDecider::ScopedAggregate scope(frame_rate_decider_.get());
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index f1ae287..324e69a9 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -153,6 +153,8 @@
 }
 
 void CompositorFrameSinkSupport::OnSurfaceDrawn(Surface* surface) {
+  if (last_drawn_frame_index_ >= surface->GetActiveFrameIndex())
+    return;
   last_drawn_frame_index_ = surface->GetActiveFrameIndex();
 }
 
@@ -457,8 +459,6 @@
     // to determine the freshness of a surface at aggregation time.
     const LocalSurfaceId& last_created_local_surface_id =
         last_created_surface_id_.local_surface_id();
-    bool last_surface_has_dependent_frame =
-        prev_surface && prev_surface->HasDependentFrame();
 
     bool child_initiated_synchronization_event =
         last_created_local_surface_id.is_valid() &&
@@ -485,14 +485,6 @@
       return SubmitResult::SURFACE_ID_DECREASED;
     }
 
-    // If the last Surface doesn't have a dependent frame, and this frame
-    // corresponds to a child-initiated synchronization event then defer this
-    // Surface until a dependent frame arrives. This throttles child submission
-    // of CompositorFrames to the parent's embedding rate.
-    const bool block_activation_on_parent =
-        child_initiated_synchronization_event &&
-        !last_surface_has_dependent_frame;
-
     // Don't recreate a surface that was previously evicted. Drop the
     // CompositorFrame and return all its resources.
     if (IsEvicted(local_surface_id)) {
@@ -502,7 +494,7 @@
     }
 
     current_surface = surface_manager_->CreateSurface(
-        weak_factory_.GetWeakPtr(), surface_info, block_activation_on_parent);
+        weak_factory_.GetWeakPtr(), surface_info);
     if (!current_surface) {
       TRACE_EVENT_INSTANT0("viz", "Surface belongs to another client",
                            TRACE_EVENT_SCOPE_THREAD);
@@ -788,17 +780,16 @@
   if (!last_activated_surface_id_.is_valid())
     return true;
 
-  Surface* surface =
-      surface_manager_->GetSurfaceForId(last_activated_surface_id_);
-  // If client has not submitted any frames, or the first frame submitted is
-  // yet to be embedded, then allow the begin-frame to be dispatched to the
-  // client.
-  if (!surface || !surface->seen_first_surface_embedding())
+  // We should never throttle BeginFrames if there is another client waiting for
+  // this client to submit a frame.
+  if (surface_manager_->HasBlockedEmbedder(frame_sink_id_))
     return true;
 
-  // If the embedded surface doesn't have an active frame, send begin frame.
-  if (!surface->HasActiveFrame())
-    return true;
+  Surface* surface =
+      surface_manager_->GetSurfaceForId(last_activated_surface_id_);
+
+  DCHECK(surface);
+  DCHECK(surface->HasActiveFrame());
 
   uint64_t active_frame_index = surface->GetActiveFrameIndex();
 
@@ -807,13 +798,8 @@
   // must be at least as large as our last drawn frame index.
   DCHECK_GE(active_frame_index, last_drawn_frame_index_);
 
-  // Determine the number of undrawn frames. If this is below our limit, send
-  // begin frame. Limit must be at least 1, as the relative ordering of
-  // renderer / browser frame submissions allows us to have one outstanding
-  // undrawn frame under normal operation.
-  constexpr uint64_t undrawn_frame_limit = 1;
   uint64_t num_undrawn_frames = active_frame_index - last_drawn_frame_index_;
-  if (num_undrawn_frames <= undrawn_frame_limit)
+  if (num_undrawn_frames <= kUndrawnFrameLimit)
     return true;
 
   // Send begin-frames if the previous begin-frame was sent more than 1 second
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index d9878f58..a6b5fd83 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -60,7 +60,13 @@
                                    const gfx::Rect& damage_rect,
                                    base::TimeTicks expected_display_time)>;
 
-  static const uint64_t kFrameIndexStart = 2;
+  static constexpr uint64_t kFrameIndexStart = 2;
+
+  // Determines maximum number of allowed undrawn frames. Once this limit is
+  // exceeded, we throttle sBeginFrames to 1 per second. Limit must be at least
+  // 1, as the relative ordering of renderer / browser frame submissions allows
+  // us to have one outstanding undrawn frame under normal operation.
+  static constexpr uint32_t kUndrawnFrameLimit = 3;
 
   CompositorFrameSinkSupport(mojom::CompositorFrameSinkClient* client,
                              FrameSinkManagerImpl* frame_sink_manager,
@@ -288,7 +294,7 @@
   // TODO(crbug.com/754872): Remove once tab capture has moved into VIZ.
   AggregatedDamageCallback aggregated_damage_callback_;
 
-  uint64_t last_frame_index_ = kFrameIndexStart;
+  uint64_t last_frame_index_ = kFrameIndexStart - 1;
 
   // The video capture clients hooking into this instance to observe frame
   // begins and damage, and then make CopyOutputRequests on the appropriate
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index 8ec698e6..14ab075 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -605,30 +605,6 @@
       mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
   EXPECT_EQ(SubmitResult::ACCEPTED, result);
 
-  // Since the Surface corresponding to |local_surface_id1| was not a dependency
-  // anywhere then the Surface corresponding to |local_surface_id2| will not
-  // activate until it becomes a dependency.
-  Surface* last_created_surface = support->GetLastCreatedSurfaceForTesting();
-  EXPECT_EQ(local_surface_id2,
-            last_created_surface->surface_id().local_surface_id());
-  EXPECT_FALSE(last_created_surface->HasActiveFrame());
-
-  SurfaceId surface_id2(kAnotherArbitraryFrameSinkId, local_surface_id2);
-  auto frame =
-      CompositorFrameBuilder()
-          .AddDefaultRenderPass()
-          .SetActivationDependencies({surface_id2})
-          .SetReferencedSurfaces({SurfaceRange(base::nullopt, surface_id2)})
-          .Build();
-  result = support_->MaybeSubmitCompositorFrame(
-      local_surface_id_, std::move(frame), base::nullopt, 0,
-      mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
-  EXPECT_EQ(SubmitResult::ACCEPTED, result);
-
-  // Submitting a CompositorFrame to the parent FrameSink with a dependency on
-  // |local_surface_id2| causes that Surface's CompositorFrame to activate.
-  EXPECT_TRUE(last_created_surface->HasActiveFrame());
-
   // LocalSurfaceId(7, 2): Parent-initiated synchronization.
   result = support->MaybeSubmitCompositorFrame(
       local_surface_id3, MakeDefaultCompositorFrame(), base::nullopt, 0,
diff --git a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
index d38c3b4..17eb480 100644
--- a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
+++ b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -2042,7 +2042,8 @@
   child_support2().SubmitCompositorFrame(child_id2.local_surface_id(),
                                          MakeDefaultCompositorFrame());
   EXPECT_TRUE(parent_surface()->HasActiveFrame());
-  EXPECT_EQ(3u, parent_surface()->GetActiveFrameIndex());
+  uint64_t expected_index = CompositorFrameSinkSupport::kFrameIndexStart;
+  EXPECT_EQ(expected_index, parent_surface()->GetActiveFrameIndex());
 }
 
 // This test verifies that SurfaceManager::GetLatestInFlightSurface returns
@@ -2126,9 +2127,7 @@
   // Verify that there is a temporary reference for child_id3.
   EXPECT_TRUE(HasTemporaryReference(child_id3));
 
-  // The surface corresponding to |child_id3| will not be activated until
-  // a parent embeds it because it's a child initiated synchronization.
-  EXPECT_EQ(GetSurfaceForId(child_id2),
+  EXPECT_EQ(GetSurfaceForId(child_id3),
             GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
 
   parent_support().SubmitCompositorFrame(
@@ -2136,8 +2135,6 @@
       MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
                           std::vector<TransferableResource>()));
 
-  EXPECT_EQ(GetSurfaceForId(child_id3),
-            GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4)));
   EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id3));
 
   // If the primary surface is active, we return it.
@@ -2831,291 +2828,6 @@
   EXPECT_EQ(parent_id, parent_support().last_activated_surface_id());
 }
 
-// If a parent CompositorFrame embeds a child Surface newer than the throttled
-// child then the throttled child surface is immediately unthrottled. This
-// unblocks the child to make progress to catch up with the parent.
-TEST_F(SurfaceSynchronizationTest,
-       ParentEmbeddingFutureChildUnblocksCurrentChildSurface) {
-  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-  const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 1, 3);
-
-  // |child_id1| Surface should immediately activate.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-
-  // |child_id2| Surface should not activate because |child_id1| was never
-  // added as a dependency by a parent.
-  child_support1().SubmitCompositorFrame(
-      child_id2.local_surface_id(),
-      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
-                          std::vector<TransferableResource>(),
-                          MakeDeadline(1u)));
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-  EXPECT_TRUE(child_surface2->has_deadline());
-
-  FrameDeadline deadline = MakeDefaultDeadline();
-  base::TimeTicks deadline_wall_time = deadline.ToWallTime();
-  EXPECT_EQ(deadline_wall_time, child_surface2->deadline_for_testing());
-
-  // The parent finally embeds a child surface that hasn't arrived which
-  // activates |child_id2|'s Surface in order for the child to make forward
-  // progress.
-  parent_support().SubmitCompositorFrame(
-      parent_id.local_surface_id(),
-      MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-}
-
-// A child surface can be blocked on its own activation dependencies and on a
-// parent embedding it as an activation dependency. Even if a child's activation
-// dependencies arrive, it may not activate until it is embedded.
-TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent) {
-  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-  const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1);
-
-  // |child_id1| Surface should immediately activate.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-
-  // |child_id2| Surface should not activate because |child_id1| was never
-  // added as a dependency by a parent AND it depends on |child_id3| which has
-  // not yet arrived.
-  child_support1().SubmitCompositorFrame(
-      child_id2.local_surface_id(),
-      MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-  EXPECT_THAT(child_surface2->activation_dependencies(),
-              UnorderedElementsAre(child_id3));
-
-  // |child_id2|'s dependency has arrived but |child_id2| Surface has not
-  // activated because it is still throttled by its parent. It will not
-  // activate until its parent arrives.
-  child_support2().SubmitCompositorFrame(child_id3.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty());
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-
-  // The parent finally embeds a |child_id2| which activates |child_id2|'s
-  // Surface.
-  parent_support().SubmitCompositorFrame(
-      parent_id.local_surface_id(),
-      MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)},
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-}
-
-// Similar to the previous test, a child surface can be blocked on its own
-// activation dependencies and on a parent embedding it as an activation
-// dependency. In this case, the parent CompositorFrame arrives first, and then
-// the activation dependency.
-TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent2) {
-  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-  const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1);
-
-  // |child_id1| Surface should immediately activate.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-
-  // |child_id2| Surface should not activate because |child_id1| was never
-  // added as a dependency by a parent AND it depends on |child_id3| which has
-  // not yet arrived.
-  child_support1().SubmitCompositorFrame(
-      child_id2.local_surface_id(),
-      MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-  EXPECT_THAT(child_surface2->activation_dependencies(),
-              UnorderedElementsAre(child_id3));
-  EXPECT_FALSE(child_surface2->HasDependentFrame());
-
-  // The parent embeds |child_id2| but |child_id2|'s Surface cannot activate
-  // until its dependencies arrive.
-  parent_support().SubmitCompositorFrame(
-      parent_id.local_surface_id(),
-      MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)},
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-  EXPECT_THAT(child_surface2->activation_dependencies(),
-              UnorderedElementsAre(child_id3));
-  EXPECT_TRUE(child_surface2->HasDependentFrame());
-
-  // |child_id2|'s dependency has arrived and |child_id2|'s Surface finally
-  // activates.
-  child_support2().SubmitCompositorFrame(child_id3.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty());
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-}
-
-// This test verifies that if a child-initiated synchronization is blocked
-// on a parent, then the frame will still activate after a deadline passes.
-TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentActivatesAfterDeadline) {
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-
-  // |child_id1| Surface should immediately activate.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-  EXPECT_FALSE(child_surface1->HasDependentFrame());
-  EXPECT_FALSE(child_surface1->has_deadline());
-
-  // |child_id2| Surface should not activate because |child_id1| was never
-  // added as a dependency by a parent.
-  child_support1().SubmitCompositorFrame(
-      child_id2.local_surface_id(),
-      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-  EXPECT_FALSE(child_surface2->HasDependentFrame());
-  EXPECT_TRUE(child_surface2->has_deadline());
-
-  for (int i = 0; i < 3; ++i) {
-    SendNextBeginFrame();
-    // There is still a looming deadline! Eeek!
-    EXPECT_TRUE(child_surface2->HasPendingFrame());
-    EXPECT_FALSE(child_surface2->HasActiveFrame());
-    EXPECT_FALSE(child_surface2->HasDependentFrame());
-    EXPECT_TRUE(child_surface2->has_deadline());
-  }
-
-  SendNextBeginFrame();
-
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-  // We pretend this is true so that subsequent CompositorFrames to the same
-  // surface do not block on the parent again.
-  EXPECT_TRUE(child_surface2->HasDependentFrame());
-  EXPECT_FALSE(child_surface2->has_deadline());
-}
-
-// This test verifies that if a child-initiated synchronization is initiated
-// with a deadline in the past, then the frame will immediately activate and
-// be marked as having a dependent frame so that it does not block again in
-// the future.
-TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentDeadlineInPast) {
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-
-  // |child_id1| Surface should immediately activate.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-  EXPECT_FALSE(child_surface1->HasDependentFrame());
-  EXPECT_FALSE(child_surface1->has_deadline());
-
-  // Pick a deadline in the near future.
-  FrameDeadline deadline = MakeDeadline(1u);
-
-  // Advance time beyond the |deadline| above.
-  SendLateBeginFrame();
-
-  // |child_id2| Surface should activate because it was submitted with a
-  // deadline in the past.
-  child_support1().SubmitCompositorFrame(
-      child_id2.local_surface_id(),
-      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
-                          std::vector<TransferableResource>(), deadline));
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-  EXPECT_FALSE(child_surface2->has_deadline());
-
-  EXPECT_TRUE(child_surface2->HasDependentFrame());
-}
-
-// A child may submit CompositorFrame corresponding to a child-initiated
-// synchronization event followed by a CompositorFrame corresponding to a
-// parent-initiated synchronization event.
-TEST_F(SurfaceSynchronizationTest,
-       ParentInitiatedAfterChildInitiatedSynchronization) {
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  // Child-initiated synchronization event:
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-  // Parent-initiated synchronizaton event:
-  const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 2);
-
-  // |child_id1| Surface should immediately activate.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-
-  // |child_id2| Surface should not activate because |child_id1| was never
-  // added as a dependency by a parent.
-  child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-  EXPECT_TRUE(child_surface2->has_deadline());
-
-  // |child_id3| Surface should activate immediately because it corresponds to a
-  // parent-initiated synchronization event.
-  child_support1().SubmitCompositorFrame(child_id3.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface3 = GetSurfaceForId(child_id3);
-  ASSERT_NE(nullptr, child_surface3);
-  EXPECT_FALSE(child_surface3->HasPendingFrame());
-  EXPECT_TRUE(child_surface3->HasActiveFrame());
-  EXPECT_FALSE(IsMarkedForDestruction(child_id3));
-}
-
 TEST_F(SurfaceSynchronizationTest, EvictSurface) {
   const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
   // Child-initiated synchronization event:
@@ -3146,159 +2858,6 @@
   EXPECT_FALSE(IsMarkedForDestruction(child_id3));
 }
 
-// If a parent CompositorFrame is blocked on the child, don't throttle child's
-// surface to avoid a deadlock. In this variation of the test, parent's
-// CompositorFrame arrives first.
-TEST_F(SurfaceSynchronizationTest, ChildNotThrottledWhenParentBlocked1) {
-  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-  const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 2);
-  const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink1, 2, 3);
-  const SurfaceId child_id5 = MakeSurfaceId(kChildFrameSink1, 2, 4);
-
-  // The parent embeds |child_surface3|. This should avoid the child getting
-  // throttled when it submits to |child_id2|.
-  parent_support().SubmitCompositorFrame(
-      parent_id.local_surface_id(),
-      MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-
-  // |child_id1| Surface should immediately activate because it's the child's
-  // first surface.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-
-  // |child_id2| would normally get throttled, but in this case it shouldn't
-  // because the parent is blocked.
-  child_support1().SubmitCompositorFrame(
-      child_id2.local_surface_id(),
-      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
-                          std::vector<TransferableResource>(),
-                          MakeDeadline(1u)));
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-
-  // After this submission, the parent will be unblocked.
-  Surface* parent_surface = GetSurfaceForId(parent_id);
-  ASSERT_NE(nullptr, parent_surface);
-  EXPECT_TRUE(parent_surface->HasPendingFrame());
-  EXPECT_FALSE(parent_surface->HasActiveFrame());
-  child_support1().SubmitCompositorFrame(child_id3.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface3 = GetSurfaceForId(child_id3);
-  ASSERT_NE(nullptr, child_surface3);
-  EXPECT_FALSE(child_surface3->HasPendingFrame());
-  EXPECT_TRUE(child_surface3->HasActiveFrame());
-  EXPECT_FALSE(parent_surface->HasPendingFrame());
-  EXPECT_TRUE(parent_surface->HasActiveFrame());
-
-  // This is the first surface after the parent got unblocked. It will not get
-  // throttled.
-  child_support1().SubmitCompositorFrame(child_id4.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface4 = GetSurfaceForId(child_id4);
-  ASSERT_NE(nullptr, child_surface4);
-  EXPECT_FALSE(child_surface4->HasPendingFrame());
-  EXPECT_TRUE(child_surface4->HasActiveFrame());
-
-  // This is the second surface after the parent got unblocked. It will get
-  // throttled.
-  child_support1().SubmitCompositorFrame(child_id5.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface5 = GetSurfaceForId(child_id5);
-  ASSERT_NE(nullptr, child_surface5);
-  EXPECT_TRUE(child_surface5->HasPendingFrame());
-  EXPECT_FALSE(child_surface5->HasActiveFrame());
-}
-
-// If a parent CompositorFrame is blocked on the child, don't throttle child's
-// surface to avoid a deadlock. In this variation of the test, child's
-// CompositorFrame arrives first.
-TEST_F(SurfaceSynchronizationTest, ChildNotThrottledWhenParentBlocked2) {
-  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-  const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 1);
-  const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink1, 2, 2);
-  const SurfaceId child_id5 = MakeSurfaceId(kChildFrameSink1, 2, 3);
-  const SurfaceId child_id6 = MakeSurfaceId(kChildFrameSink1, 2, 4);
-
-  // |child_id1| Surface should immediately activate.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-
-  // |child_id2| Surface should not activate because |child_id1| was never
-  // added as a dependency by a parent.
-  child_support1().SubmitCompositorFrame(
-      child_id2.local_surface_id(),
-      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
-                          std::vector<TransferableResource>(),
-                          MakeDeadline(1u)));
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-  EXPECT_TRUE(child_surface2->has_deadline());
-
-  FrameDeadline deadline = MakeDefaultDeadline();
-  base::TimeTicks deadline_wall_time = deadline.ToWallTime();
-  EXPECT_EQ(deadline_wall_time, child_surface2->deadline_for_testing());
-
-  // The parent gets blocked on the child. The child should get unthrottled to
-  // avoid deadlocks.
-  parent_support().SubmitCompositorFrame(
-      parent_id.local_surface_id(),
-      MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)},
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-
-  // After this submission, the parent will be unblocked.
-  Surface* parent_surface = GetSurfaceForId(parent_id);
-  ASSERT_NE(nullptr, parent_surface);
-  EXPECT_TRUE(parent_surface->HasPendingFrame());
-  EXPECT_FALSE(parent_surface->HasActiveFrame());
-  child_support1().SubmitCompositorFrame(child_id4.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface4 = GetSurfaceForId(child_id4);
-  ASSERT_NE(nullptr, child_surface4);
-  EXPECT_FALSE(child_surface4->HasPendingFrame());
-  EXPECT_TRUE(child_surface4->HasActiveFrame());
-  EXPECT_FALSE(parent_surface->HasPendingFrame());
-  EXPECT_TRUE(parent_surface->HasActiveFrame());
-
-  // This is the first surface after the parent got unblocked. It will not get
-  // throttled.
-  child_support1().SubmitCompositorFrame(child_id5.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface5 = GetSurfaceForId(child_id5);
-  ASSERT_NE(nullptr, child_surface5);
-  EXPECT_FALSE(child_surface5->HasPendingFrame());
-  EXPECT_TRUE(child_surface5->HasActiveFrame());
-
-  // This is the second surface after the parent got unblocked. It will get
-  // throttled.
-  child_support1().SubmitCompositorFrame(child_id6.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface6 = GetSurfaceForId(child_id6);
-  ASSERT_NE(nullptr, child_surface6);
-  EXPECT_TRUE(child_surface6->HasPendingFrame());
-  EXPECT_FALSE(child_surface6->HasActiveFrame());
-}
-
 // Tests that in cases where a pending-deletion surface (surface A) is
 // activated during anothother surface (surface B)'s deletion, we don't attempt
 // to delete surface A twice.
@@ -3306,6 +2865,7 @@
   const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
   // Child-initiated synchronization event:
   const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
+  const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1);
 
   // Submit a CompositorFrame to |child_id1|.
   child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
@@ -3318,8 +2878,10 @@
   EXPECT_TRUE(child_surface1->HasActiveFrame());
 
   // Submit a CompositorFrame to |child_id2|.
-  child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
+  child_support1().SubmitCompositorFrame(
+      child_id2.local_surface_id(),
+      MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(),
+                          std::vector<TransferableResource>()));
 
   // Child 2 should not yet be active.
   Surface* child_surface2 = GetSurfaceForId(child_id2);
@@ -3488,72 +3050,6 @@
   EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2));
 }
 
-// This test verifies that the child gets unthrottled when the parent embeds the
-// second last surface. https://crbug.com/898460
-TEST_F(SurfaceSynchronizationTest,
-       ChildUnthrottledWhenSecondLastSurfaceEmbedded) {
-  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
-  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
-  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
-  const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 1, 3);
-  const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink1, 1, 4);
-
-  // |child_id1| Surface should immediately activate because one unembedded
-  // surface is allowed.
-  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
-                                         MakeDefaultCompositorFrame());
-  Surface* child_surface1 = GetSurfaceForId(child_id1);
-  ASSERT_NE(nullptr, child_surface1);
-  EXPECT_FALSE(child_surface1->HasPendingFrame());
-  EXPECT_TRUE(child_surface1->HasActiveFrame());
-
-  // |child_id2| Surface should not activate because now there are two surfaces
-  // not embedded by the parent makes child throttling kick in.
-  child_support1().SubmitCompositorFrame(
-      child_id2.local_surface_id(),
-      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
-                          std::vector<TransferableResource>(),
-                          MakeDeadline(1u)));
-  Surface* child_surface2 = GetSurfaceForId(child_id2);
-  ASSERT_NE(nullptr, child_surface2);
-  EXPECT_TRUE(child_surface2->HasPendingFrame());
-  EXPECT_FALSE(child_surface2->HasActiveFrame());
-  EXPECT_TRUE(child_surface2->has_deadline());
-
-  // The parent embeds |child_id1| and blocks. Both |child_id2| should activate
-  // because now again there is only one surface not embedded by the parent.
-  parent_support().SubmitCompositorFrame(
-      parent_id.local_surface_id(),
-      MakeCompositorFrame({child_id1}, {SurfaceRange(base::nullopt, child_id1)},
-                          std::vector<TransferableResource>(),
-                          MakeDefaultDeadline()));
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-
-  // The child submits to |child_id3|. Throttling should still not kick in due
-  // to https://crbug.com/951992.
-  child_support1().SubmitCompositorFrame(
-      child_id3.local_surface_id(),
-      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
-                          std::vector<TransferableResource>(),
-                          MakeDeadline(1u)));
-  Surface* child_surface3 = GetSurfaceForId(child_id3);
-  ASSERT_NE(nullptr, child_surface3);
-  EXPECT_FALSE(child_surface3->HasPendingFrame());
-  EXPECT_TRUE(child_surface3->HasActiveFrame());
-
-  // The child submits to |child_id4|. Throttling should kick in.
-  child_support1().SubmitCompositorFrame(
-      child_id4.local_surface_id(),
-      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
-                          std::vector<TransferableResource>(),
-                          MakeDeadline(1u)));
-  Surface* child_surface4 = GetSurfaceForId(child_id4);
-  ASSERT_NE(nullptr, child_surface4);
-  EXPECT_TRUE(child_surface4->HasPendingFrame());
-  EXPECT_FALSE(child_surface4->HasActiveFrame());
-}
-
 // Verifies that the value of last active surface is correct after embed token
 // changes. https://crbug.com/967012
 TEST_F(SurfaceSynchronizationTest,
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 47e3bd2..eed28b34 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -29,12 +29,10 @@
 Surface::Surface(const SurfaceInfo& surface_info,
                  SurfaceManager* surface_manager,
                  SurfaceAllocationGroup* allocation_group,
-                 base::WeakPtr<SurfaceClient> surface_client,
-                 bool block_activation_on_parent)
+                 base::WeakPtr<SurfaceClient> surface_client)
     : surface_info_(surface_info),
       surface_manager_(surface_manager),
       surface_client_(std::move(surface_client)),
-      block_activation_on_parent_(block_activation_on_parent),
       allocation_group_(allocation_group),
       weak_factory_(this) {
   TRACE_EVENT_ASYNC_BEGIN1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
@@ -167,18 +165,6 @@
   }
 }
 
-void Surface::OnSurfaceDependencyAdded() {
-  if (seen_first_surface_dependency_)
-    return;
-
-  seen_first_surface_dependency_ = true;
-  if (!activation_dependencies_.empty() || !pending_frame_data_)
-    return;
-
-  // All blockers have been cleared. The surface can be activated now.
-  ActivatePendingFrame();
-}
-
 void Surface::SetIsFallbackAndMaybeActivate() {
   is_fallback_ = true;
   if (HasPendingFrame())
@@ -224,16 +210,7 @@
   // regardless of whether it's pending or active.
   surface_client_->ReceiveFromChild(frame.resource_list);
 
-  if (!seen_first_surface_dependency_) {
-    // We should not throttle this client if there is another client blocked on
-    // it, in order to avoid deadlocks.
-    seen_first_surface_dependency_ = allocation_group_->HasBlockedEmbedder();
-  }
-
-  bool block_activation =
-      block_activation_on_parent_ && !seen_first_surface_dependency_;
-
-  if (!block_activation && activation_dependencies_.empty()) {
+  if (activation_dependencies_.empty()) {
     // If there are no blockers, then immediately activate the frame.
     ActivateFrame(FrameData(std::move(frame), frame_index), base::nullopt);
   } else {
@@ -283,9 +260,7 @@
   DCHECK(activation_dependencies_.count(activation_dependency));
   activation_dependencies_.erase(activation_dependency);
   blocking_allocation_groups_.erase(group);
-  bool block_activation =
-      block_activation_on_parent_ && !seen_first_surface_dependency_;
-  if (block_activation || !activation_dependencies_.empty())
+  if (!activation_dependencies_.empty())
     return;
   // All blockers have been cleared. The surface can be activated now.
   ActivatePendingFrame();
@@ -299,11 +274,6 @@
   // of blockers.
   activation_dependencies_.clear();
 
-  // We treat an activation (by deadline) as being the equivalent of a parent
-  // embedding the surface in order to avoid blocking future frames to the same
-  // surface.
-  seen_first_surface_dependency_ = true;
-
   ActivatePendingFrame();
 }
 
@@ -479,13 +449,9 @@
   const FrameDeadline& deadline = current_frame.metadata.deadline;
   uint32_t deadline_in_frames = deadline.deadline_in_frames();
 
-  bool block_activation =
-      block_activation_on_parent_ && !seen_first_surface_dependency_;
-
   // If no default deadline is available then all deadlines are treated as
   // effectively infinite deadlines.
-  if (!default_deadline || deadline.use_default_lower_bound_deadline() ||
-      block_activation) {
+  if (!default_deadline || deadline.use_default_lower_bound_deadline()) {
     deadline_in_frames = std::max(
         deadline_in_frames,
         default_deadline.value_or(std::numeric_limits<uint32_t>::max()));
@@ -503,12 +469,9 @@
   blocking_allocation_groups_.clear();
   activation_dependencies_.clear();
 
-  // If the client has specified a deadline of zero and we don't need to block
-  // on the parent, there is no need to figure out the activation dependencies,
-  // since the frame will activate immediately.
-  bool block_activation =
-      block_activation_on_parent_ && !seen_first_surface_dependency_;
-  if (!block_activation && current_frame.metadata.deadline.IsZero())
+  // If the client has specified a deadline of zero, there is no need to figure
+  // out the activation dependencies since the frame will activate immediately.
+  if (current_frame.metadata.deadline.IsZero())
     return;
 
   std::vector<SurfaceAllocationGroup*> new_blocking_allocation_groups;
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h
index ce9c8d94..8c9ee36 100644
--- a/components/viz/service/surfaces/surface.h
+++ b/components/viz/service/surfaces/surface.h
@@ -83,8 +83,7 @@
   Surface(const SurfaceInfo& surface_info,
           SurfaceManager* surface_manager,
           SurfaceAllocationGroup* allocation_group,
-          base::WeakPtr<SurfaceClient> surface_client,
-          bool block_activation_on_parent);
+          base::WeakPtr<SurfaceClient> surface_client);
   ~Surface();
 
   void SetDependencyDeadline(
@@ -120,10 +119,6 @@
     return surface_client_ ? surface_client_->NeedsSyncTokens() : false;
   }
 
-  bool block_activation_on_parent() const {
-    return block_activation_on_parent_;
-  }
-
   // Returns false if |frame| is invalid.
   // |frame_rejected_callback| will be called once if the frame will not be
   // displayed.
@@ -200,10 +195,6 @@
     return HasActiveFrame() && !active_frame_data_->frame_acked;
   }
 
-  // Returns true if at any point, another Surface's CompositorFrame has
-  // depended on this Surface.
-  bool HasDependentFrame() const { return seen_first_surface_dependency_; }
-
   bool seen_first_surface_embedding() const {
     return seen_first_surface_embedding_;
   }
@@ -217,9 +208,6 @@
   // referenced SurfaceRange.
   void OnChildActivatedForActiveFrame(const SurfaceId& surface_id);
 
-  // Called when this surface is embedded by another Surface's CompositorFrame.
-  void OnSurfaceDependencyAdded();
-
   // Called when the embedder of this surface has been activated and therefore
   // this surface should activate too by deadline inheritance.
   void ActivatePendingFrameForInheritedDeadline();
@@ -312,13 +300,6 @@
   base::Optional<FrameData> active_frame_data_;
   bool seen_first_frame_activation_ = false;
   bool seen_first_surface_embedding_ = false;
-  // Indicates whether another surface adds this surface as a dependency. When
-  // set to true, this surface will be unthrottled and the surface that is
-  // created after it will also not be throttled.
-  bool seen_first_surface_dependency_ = false;
-  // When false, this surface will not be subject to child throttling even if
-  // it's not embedded yet.
-  bool block_activation_on_parent_ = false;
 
   // A set of all valid SurfaceIds contained |last_surface_id_for_range_| to
   // avoid recompution.
diff --git a/components/viz/service/surfaces/surface_allocation_group.cc b/components/viz/service/surfaces/surface_allocation_group.cc
index 75d414ef..3104b56 100644
--- a/components/viz/service/surfaces/surface_allocation_group.cc
+++ b/components/viz/service/surfaces/surface_allocation_group.cc
@@ -241,25 +241,11 @@
   auto it = FindLatestSurfaceUpTo(surface_id);
   if (it == surfaces_.end())
     return;
-  // Notify that |*it| is now embedded. This might unblock |*it| if it was
-  // blocked due to child throttling.
-  (*it)->OnSurfaceDependencyAdded();
   // If |surface_id| does not exist yet, notify the surface immediately prior to
   // it that it is a fallback. This might activate the surface immediately
   // because fallback surfaces never block.
   if ((*it)->surface_id() != surface_id)
     (*it)->SetIsFallbackAndMaybeActivate();
-  // Since child throttling allows up to two unembedded surfaces, the surface
-  // immediately after |it| should not be throttled even if it is not embedded
-  // yet.
-  ++it;
-  if (it == surfaces_.end())
-    return;
-  // This ensures the next surface has its seen_first_surface_dependency_ bit
-  // set so that throttling doesn't kick in until 3 surfaces after the surface
-  // that was just embedded. We see regression if throttling kicks in sooner.
-  // See https://crbug.com/951992.
-  (*it)->OnSurfaceDependencyAdded();
 }
 
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface_manager.cc b/components/viz/service/surfaces/surface_manager.cc
index 17f9a1c..3f533bf 100644
--- a/components/viz/service/surfaces/surface_manager.cc
+++ b/components/viz/service/surfaces/surface_manager.cc
@@ -103,8 +103,7 @@
 
 Surface* SurfaceManager::CreateSurface(
     base::WeakPtr<SurfaceClient> surface_client,
-    const SurfaceInfo& surface_info,
-    bool block_activation_on_parent) {
+    const SurfaceInfo& surface_info) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(surface_info.is_valid());
   DCHECK(surface_client);
@@ -121,9 +120,8 @@
   if (!allocation_group)
     return nullptr;
 
-  std::unique_ptr<Surface> surface =
-      std::make_unique<Surface>(surface_info, this, allocation_group,
-                                surface_client, block_activation_on_parent);
+  std::unique_ptr<Surface> surface = std::make_unique<Surface>(
+      surface_info, this, allocation_group, surface_client);
   surface->SetDependencyDeadline(
       std::make_unique<SurfaceDependencyDeadline>(tick_clock_));
   surface_map_[surface_info.id()] = std::move(surface);
@@ -633,4 +631,17 @@
   allocation_groups_need_garbage_collection_ = false;
 }
 
+bool SurfaceManager::HasBlockedEmbedder(
+    const FrameSinkId& frame_sink_id) const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  auto it = frame_sink_id_to_allocation_groups_.find(frame_sink_id);
+  if (it == frame_sink_id_to_allocation_groups_.end())
+    return false;
+  for (SurfaceAllocationGroup* group : it->second) {
+    if (group->HasBlockedEmbedder())
+      return true;
+  }
+  return false;
+}
+
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface_manager.h b/components/viz/service/surfaces/surface_manager.h
index 230b699..bbe658bb 100644
--- a/components/viz/service/surfaces/surface_manager.h
+++ b/components/viz/service/surfaces/surface_manager.h
@@ -79,8 +79,7 @@
   // dependencies are satisfied, and it is not reachable from the root surface.
   // A temporary reference will be added to the new Surface.
   Surface* CreateSurface(base::WeakPtr<SurfaceClient> surface_client,
-                         const SurfaceInfo& surface_info,
-                         bool block_activation_on_parent);
+                         const SurfaceInfo& surface_info);
 
   // Marks |surface_id| for destruction. The surface will get destroyed when
   // it's not reachable from the root or any other surface that is not marked
@@ -194,6 +193,10 @@
   // collection.
   void SetAllocationGroupsNeedGarbageCollection();
 
+  // Returns whether there is any surface blocked on a surface from
+  // |frame_sink_id|.
+  bool HasBlockedEmbedder(const FrameSinkId& frame_sink_id) const;
+
  private:
   friend class CompositorFrameSinkSupportTest;
   friend class FrameSinkManagerTest;
diff --git a/content/browser/android/selection/OWNERS b/content/browser/android/selection/OWNERS
index 4943acc..395fd997 100644
--- a/content/browser/android/selection/OWNERS
+++ b/content/browser/android/selection/OWNERS
@@ -1 +1 @@
-amaralp@chromium.org
+ctzsm@chromium.org
diff --git a/content/browser/dom_storage/dom_storage_area.cc b/content/browser/dom_storage/dom_storage_area.cc
index 590de95..4fdd70a7 100644
--- a/content/browser/dom_storage/dom_storage_area.cc
+++ b/content/browser/dom_storage/dom_storage_area.cc
@@ -524,9 +524,7 @@
   if (!GetCurrentCommitBatch()) {
     commit_batches_.emplace_front(CommitBatchHolder(
         CommitBatchHolder::TYPE_CURRENT_BATCH, new CommitBatch()));
-    BrowserThread::PostAfterStartupTask(
-        FROM_HERE, task_runner_,
-        base::BindOnce(&DOMStorageArea::StartCommitTimer, this));
+    StartCommitTimer();
   }
   return GetCurrentCommitBatch()->batch.get();
 }
diff --git a/content/browser/dom_storage/storage_area_impl.cc b/content/browser/dom_storage/storage_area_impl.cc
index 4d87523f..fd55c52 100644
--- a/content/browser/dom_storage/storage_area_impl.cc
+++ b/content/browser/dom_storage/storage_area_impl.cc
@@ -724,10 +724,7 @@
   DCHECK(database_);
 
   commit_batch_.reset(new CommitBatch());
-  BrowserThread::PostAfterStartupTask(
-      FROM_HERE, base::ThreadTaskRunnerHandle::Get(),
-      base::BindOnce(&StorageAreaImpl::StartCommitTimer,
-                     weak_ptr_factory_.GetWeakPtr()));
+  StartCommitTimer();
 }
 
 void StorageAreaImpl::StartCommitTimer() {
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index bfbc2ea..9994201 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -549,6 +549,7 @@
       params.frame_tree_node_id =
           RenderFrameHost::GetFrameTreeNodeIdForRoutingId(
               info.render_process_id, info.render_frame_id);
+      params.from_download_cross_origin_redirect = true;
       web_contents->GetController().LoadURLWithParams(params);
     }
     if (info.request_handle)
diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc
index 7cd3beec..493f622 100644
--- a/content/browser/download/download_resource_handler.cc
+++ b/content/browser/download/download_resource_handler.cc
@@ -124,6 +124,7 @@
                         const std::vector<GURL> url_chain,
                         const Referrer& referrer,
                         bool has_user_gesture,
+                        bool from_download_cross_origin_redirect,
                         const ResourceRequestInfo::WebContentsGetter& wc_getter,
                         int frame_tree_node_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -135,6 +136,8 @@
     params.referrer = referrer;
     params.redirect_chain = url_chain;
     params.frame_tree_node_id = frame_tree_node_id;
+    params.from_download_cross_origin_redirect =
+        from_download_cross_origin_redirect;
     web_contents->GetController().LoadURLWithParams(params);
   }
 }
@@ -210,6 +213,7 @@
                      Referrer::NetReferrerPolicyToBlinkReferrerPolicy(
                          redirect_info.new_referrer_policy)),
             GetRequestInfo()->HasUserGesture(),
+            true /* from_download_cross_origin_redirect */,
             GetRequestInfo()->GetWebContentsGetterForRequest(),
             GetRequestInfo()->frame_tree_node_id()));
     controller->Cancel();
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index f50c1283..031b30e 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -3141,10 +3141,14 @@
   // extra_headers in params are \n separated; NavigationRequests want \r\n.
   std::string extra_headers_crlf;
   base::ReplaceChars(params.extra_headers, "\n", "\r\n", &extra_headers_crlf);
-  return NavigationRequest::CreateBrowserInitiated(
+
+  auto navigation_request = NavigationRequest::CreateBrowserInitiated(
       node, common_params, commit_params, !params.is_renderer_initiated,
       extra_headers_crlf, *frame_entry, entry, request_body,
       params.navigation_ui_data ? params.navigation_ui_data->Clone() : nullptr);
+  navigation_request->set_from_download_cross_origin_redirect(
+      params.from_download_cross_origin_redirect);
+  return navigation_request;
 }
 
 std::unique_ptr<NavigationRequest>
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 7c0ab24..86b9134 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -452,6 +452,10 @@
   return navigation_request_->navigation_entry_offset();
 }
 
+bool NavigationHandleImpl::FromDownloadCrossOriginRedirect() {
+  return navigation_request_->from_download_cross_origin_redirect();
+}
+
 bool NavigationHandleImpl::IsSignedExchangeInnerResponse() {
   return navigation_request_->response()
              ? navigation_request_->response()
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index 2a97d1a..3172fad 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -112,6 +112,7 @@
   const base::Optional<url::Origin>& GetInitiatorOrigin() override;
   bool IsSameProcess() override;
   int GetNavigationEntryOffset() override;
+  bool FromDownloadCrossOriginRedirect() override;
 
   // Returns the NavigationRequest which owns this NavigationHandle.
   NavigationRequest* navigation_request() { return navigation_request_; }
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc
index f9d0d4ba..1a5ba30 100644
--- a/content/browser/frame_host/navigation_handle_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -6,6 +6,7 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "build/build_config.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/ssl_status.h"
@@ -306,9 +307,16 @@
             navigation->GetNavigationHandle()->GetNetErrorCode());
 }
 
+// Flaky on Android. https://crbug.com/970815
+#if defined(OS_ANDROID)
+#define MAYBE_CancelDeferredWillStart DISABLED_CancelDeferredWillStart
+#else
+#define MAYBE_CancelDeferredWillStart CancelDeferredWillStart
+#endif
+
 // Checks that a navigation deferred during WillStartRequest can be properly
 // cancelled.
-TEST_F(NavigationHandleImplTest, CancelDeferredWillStart) {
+TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillStart) {
   TestNavigationThrottle* test_throttle =
       CreateTestNavigationThrottle(NavigationThrottle::DEFER);
   EXPECT_EQ(NavigationRequest::INITIAL, state());
@@ -329,9 +337,16 @@
   EXPECT_TRUE(call_counts_match(test_throttle, 1, 0, 0, 0));
 }
 
+// Flaky on Android. https://crbug.com/970815
+#if defined(OS_ANDROID)
+#define MAYBE_CancelDeferredWillRedirect DISABLED_CancelDeferredWillRedirect
+#else
+#define MAYBE_CancelDeferredWillRedirect CancelDeferredWillRedirect
+#endif
+
 // Checks that a navigation deferred during WillRedirectRequest can be properly
 // cancelled.
-TEST_F(NavigationHandleImplTest, CancelDeferredWillRedirect) {
+TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillRedirect) {
   TestNavigationThrottle* test_throttle =
       CreateTestNavigationThrottle(NavigationThrottle::DEFER);
   EXPECT_EQ(NavigationRequest::INITIAL, state());
@@ -352,9 +367,16 @@
   EXPECT_TRUE(call_counts_match(test_throttle, 0, 1, 0, 0));
 }
 
+// Flaky on Android. https://crbug.com/970815
+#if defined(OS_ANDROID)
+#define MAYBE_CancelDeferredWillFail DISABLED_CancelDeferredWillFail
+#else
+#define MAYBE_CancelDeferredWillFail CancelDeferredWillFail
+#endif
+
 // Checks that a navigation deferred during WillFailRequest can be properly
 // cancelled.
-TEST_F(NavigationHandleImplTest, CancelDeferredWillFail) {
+TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillFail) {
   TestNavigationThrottle* test_throttle = CreateTestNavigationThrottle(
       TestNavigationThrottle::WILL_FAIL_REQUEST, NavigationThrottle::DEFER);
   EXPECT_EQ(NavigationRequest::INITIAL, state());
@@ -379,8 +401,17 @@
   EXPECT_TRUE(call_counts_match(test_throttle, 1, 0, 1, 0));
 }
 
+// Flaky on Android. https://crbug.com/970815
+#if defined(OS_ANDROID)
+#define MAYBE_CancelDeferredWillRedirectNoIgnore \
+  DISABLED_CancelDeferredWillRedirectNoIgnore
+#else
+#define MAYBE_CancelDeferredWillRedirectNoIgnore \
+  CancelDeferredWillRedirectNoIgnore
+#endif
+
 // Checks that a navigation deferred can be canceled and not ignored.
-TEST_F(NavigationHandleImplTest, CancelDeferredWillRedirectNoIgnore) {
+TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillRedirectNoIgnore) {
   TestNavigationThrottle* test_throttle =
       CreateTestNavigationThrottle(NavigationThrottle::DEFER);
   EXPECT_EQ(NavigationRequest::INITIAL, state());
@@ -401,9 +432,17 @@
   EXPECT_TRUE(call_counts_match(test_throttle, 1, 0, 0, 0));
 }
 
+// Flaky on Android. https://crbug.com/970815
+#if defined(OS_ANDROID)
+#define MAYBE_CancelDeferredWillFailNoIgnore \
+  DISABLED_CancelDeferredWillFailNoIgnore
+#else
+#define MAYBE_CancelDeferredWillFailNoIgnore CancelDeferredWillFailNoIgnore
+#endif
+
 // Checks that a navigation deferred by WillFailRequest can be canceled and not
 // ignored.
-TEST_F(NavigationHandleImplTest, CancelDeferredWillFailNoIgnore) {
+TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillFailNoIgnore) {
   TestNavigationThrottle* test_throttle = CreateTestNavigationThrottle(
       TestNavigationThrottle::WILL_FAIL_REQUEST, NavigationThrottle::DEFER);
   EXPECT_EQ(NavigationRequest::INITIAL, state());
@@ -490,11 +529,21 @@
 
 }  // namespace
 
+// Flaky on Android. https://crbug.com/970815
+#if defined(OS_ANDROID)
+#define MAYBE_WillFailRequestCanAccessRenderFrameHost \
+  DISABLED_WillFailRequestCanAccessRenderFrameHost
+#else
+#define MAYBE_WillFailRequestCanAccessRenderFrameHost \
+  WillFailRequestCanAccessRenderFrameHost
+#endif
+
 // Verify that the NavigationHandle::GetRenderFrameHost() can be retrieved by a
 // throttle in WillFailRequest(), as well as after deferring the failure.  This
 // is allowed, since at that point the final RenderFrameHost will have already
 // been chosen. See https://crbug.com/817881.
-TEST_F(NavigationHandleImplTest, WillFailRequestCanAccessRenderFrameHost) {
+TEST_F(NavigationHandleImplTest,
+       MAYBE_WillFailRequestCanAccessRenderFrameHost) {
   std::unique_ptr<ContentBrowserClient> client(
       new ThrottleTestContentBrowserClient);
   ContentBrowserClient* old_browser_client =
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 523b619..50e22d8 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -406,6 +406,9 @@
     return previous_url_;
   }
 
+  bool from_download_cross_origin_redirect() const {
+    return from_download_cross_origin_redirect_;
+  }
 
 #if defined(OS_ANDROID)
   // Returns a reference to |navigation_handle_| Java counterpart. It is used
@@ -421,6 +424,11 @@
 
   Referrer& sanitized_referrer() { return sanitized_referrer_; }
 
+  void set_from_download_cross_origin_redirect(
+      bool from_download_cross_origin_redirect) {
+    from_download_cross_origin_redirect_ = from_download_cross_origin_redirect;
+  }
+
   // This should be a private method. The only valid reason to be used
   // outside of the class constructor is in the case of an initial history
   // navigation in a subframe. This allows a browser-initiated NavigationRequest
@@ -833,6 +841,10 @@
 
   bool was_redirected_ = false;
 
+  // Whether this navigation was triggered by a x-origin redirect following a
+  // prior (most likely <a download>) download attempt.
+  bool from_download_cross_origin_redirect_ = false;
+
   // Used when SignedExchangeSubresourcePrefetch is enabled to hold the
   // prefetched signed exchanges. This is shared with the navigation initiator's
   // RenderFrameHostImpl. This also means that only the navigations that were
diff --git a/content/browser/gpu/gpu_data_manager_impl.cc b/content/browser/gpu/gpu_data_manager_impl.cc
index 13c5feb..da6ef878 100644
--- a/content/browser/gpu/gpu_data_manager_impl.cc
+++ b/content/browser/gpu/gpu_data_manager_impl.cc
@@ -25,12 +25,12 @@
   private_->BlacklistWebGLForTesting();
 }
 
-gpu::GPUInfo GpuDataManagerImpl::GetGPUInfo() const {
+gpu::GPUInfo GpuDataManagerImpl::GetGPUInfo() {
   base::AutoLock auto_lock(lock_);
   return private_->GetGPUInfo();
 }
 
-bool GpuDataManagerImpl::GpuAccessAllowed(std::string* reason) const {
+bool GpuDataManagerImpl::GpuAccessAllowed(std::string* reason) {
   base::AutoLock auto_lock(lock_);
   return private_->GpuAccessAllowed(reason);
 }
@@ -40,7 +40,7 @@
   private_->RequestCompleteGpuInfoIfNeeded();
 }
 
-bool GpuDataManagerImpl::IsEssentialGpuInfoAvailable() const {
+bool GpuDataManagerImpl::IsEssentialGpuInfoAvailable() {
   base::AutoLock auto_lock(lock_);
   return private_->IsEssentialGpuInfoAvailable();
 }
@@ -57,7 +57,7 @@
 }
 
 void GpuDataManagerImpl::RequestVideoMemoryUsageStatsUpdate(
-    VideoMemoryUsageStatsCallback callback) const {
+    VideoMemoryUsageStatsCallback callback) {
   base::AutoLock auto_lock(lock_);
   private_->RequestVideoMemoryUsageStatsUpdate(std::move(callback));
 }
@@ -79,13 +79,13 @@
   private_->DisableHardwareAcceleration();
 }
 
-bool GpuDataManagerImpl::HardwareAccelerationEnabled() const {
+bool GpuDataManagerImpl::HardwareAccelerationEnabled() {
   base::AutoLock auto_lock(lock_);
   return private_->HardwareAccelerationEnabled();
 }
 
 void GpuDataManagerImpl::AppendGpuCommandLine(base::CommandLine* command_line,
-                                              GpuProcessKind kind) const {
+                                              GpuProcessKind kind) {
   base::AutoLock auto_lock(lock_);
   private_->AppendGpuCommandLine(command_line, kind);
 }
diff --git a/content/browser/gpu/gpu_data_manager_impl.h b/content/browser/gpu/gpu_data_manager_impl.h
index 9c3f898..dfbc831 100644
--- a/content/browser/gpu/gpu_data_manager_impl.h
+++ b/content/browser/gpu/gpu_data_manager_impl.h
@@ -46,12 +46,12 @@
 
   // GpuDataManager implementation.
   void BlacklistWebGLForTesting() override;
-  gpu::GPUInfo GetGPUInfo() const override;
-  bool GpuAccessAllowed(std::string* reason) const override;
+  gpu::GPUInfo GetGPUInfo() override;
+  bool GpuAccessAllowed(std::string* reason) override;
   void RequestCompleteGpuInfoIfNeeded() override;
-  bool IsEssentialGpuInfoAvailable() const override;
+  bool IsEssentialGpuInfoAvailable() override;
   void RequestVideoMemoryUsageStatsUpdate(
-      VideoMemoryUsageStatsCallback callback) const override;
+      VideoMemoryUsageStatsCallback callback) override;
   // TODO(kbr): the threading model for the GpuDataManagerObservers is
   // not well defined, and it's impossible for callers to correctly
   // delete observers from anywhere except in one of the observer's
@@ -60,9 +60,9 @@
   void AddObserver(GpuDataManagerObserver* observer) override;
   void RemoveObserver(GpuDataManagerObserver* observer) override;
   void DisableHardwareAcceleration() override;
-  bool HardwareAccelerationEnabled() const override;
+  bool HardwareAccelerationEnabled() override;
   void AppendGpuCommandLine(base::CommandLine* command_line,
-                            GpuProcessKind kind) const override;
+                            GpuProcessKind kind) override;
 
   void RequestGpuSupportedRuntimeVersion() const;
   bool GpuProcessStartAllowed() const;
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index 6238102..76fa8b65 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -407,7 +407,7 @@
   return display_info;
 }
 
-std::string GetProfileName(gpu::VideoCodecProfile profile) {
+const char* GetProfileName(gpu::VideoCodecProfile profile) {
   switch (profile) {
     case gpu::VIDEO_CODEC_PROFILE_UNKNOWN:
       return "unknown";
@@ -457,6 +457,10 @@
       return "dolby vision profile 5";
     case gpu::DOLBYVISION_PROFILE7:
       return "dolby vision profile 7";
+    case gpu::DOLBYVISION_PROFILE8:
+      return "dolby vision profile 8";
+    case gpu::DOLBYVISION_PROFILE9:
+      return "dolby vision profile 9";
     case gpu::THEORAPROFILE_ANY:
       return "theora";
     case gpu::AV1PROFILE_PROFILE_MAIN:
@@ -476,8 +480,8 @@
 
   for (const auto& profile :
        gpu_info.video_decode_accelerator_capabilities.supported_profiles) {
-    std::string codec_string = base::StringPrintf(
-        "Decode %s", GetProfileName(profile.profile).c_str());
+    std::string codec_string =
+        base::StringPrintf("Decode %s", GetProfileName(profile.profile));
     std::string resolution_string = base::StringPrintf(
         "up to %s pixels %s", profile.max_resolution.ToString().c_str(),
         profile.encrypted_only ? "(encrypted)" : "");
@@ -486,8 +490,8 @@
 
   for (const auto& profile :
        gpu_info.video_encode_accelerator_supported_profiles) {
-    std::string codec_string = base::StringPrintf(
-        "Encode %s", GetProfileName(profile.profile).c_str());
+    std::string codec_string =
+        base::StringPrintf("Encode %s", GetProfileName(profile.profile));
     std::string resolution_string = base::StringPrintf(
         "up to %s pixels and/or %.3f fps",
         profile.max_resolution.ToString().c_str(),
diff --git a/content/browser/host_zoom_map_impl.cc b/content/browser/host_zoom_map_impl.cc
index d4a3e7ec..3ce022d 100644
--- a/content/browser/host_zoom_map_impl.cc
+++ b/content/browser/host_zoom_map_impl.cc
@@ -150,7 +150,7 @@
 }
 
 bool HostZoomMapImpl::HasZoomLevel(const std::string& scheme,
-                                   const std::string& host) const {
+                                   const std::string& host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto scheme_iterator(scheme_host_zoom_levels_.find(scheme));
 
@@ -162,9 +162,8 @@
   return base::ContainsKey(zoom_levels, host);
 }
 
-double HostZoomMapImpl::GetZoomLevelForHostAndScheme(
-    const std::string& scheme,
-    const std::string& host) const {
+double HostZoomMapImpl::GetZoomLevelForHostAndScheme(const std::string& scheme,
+                                                     const std::string& host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto scheme_iterator(scheme_host_zoom_levels_.find(scheme));
   if (scheme_iterator != scheme_host_zoom_levels_.end()) {
@@ -176,7 +175,7 @@
   return GetZoomLevelForHost(host);
 }
 
-HostZoomMap::ZoomLevelVector HostZoomMapImpl::GetAllZoomLevels() const {
+HostZoomMap::ZoomLevelVector HostZoomMapImpl::GetAllZoomLevels() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   HostZoomMap::ZoomLevelVector result;
   result.reserve(host_zoom_levels_.size() + scheme_host_zoom_levels_.size());
@@ -266,7 +265,7 @@
   zoom_level_changed_callbacks_.Notify(change);
 }
 
-double HostZoomMapImpl::GetDefaultZoomLevel() const {
+double HostZoomMapImpl::GetDefaultZoomLevel() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return default_zoom_level_;
 }
@@ -341,7 +340,7 @@
 }
 
 double HostZoomMapImpl::GetZoomLevelForWebContents(
-    WebContentsImpl* web_contents_impl) const {
+    WebContentsImpl* web_contents_impl) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   int render_process_id =
       web_contents_impl->GetRenderViewHost()->GetProcess()->GetID();
@@ -432,7 +431,7 @@
 }
 
 bool HostZoomMapImpl::UsesTemporaryZoomLevel(int render_process_id,
-                                             int render_view_id) const {
+                                             int render_view_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderViewKey key(render_process_id, render_view_id);
   return base::ContainsKey(temporary_zoom_levels_, key);
@@ -469,7 +468,7 @@
 
 double HostZoomMapImpl::GetZoomLevelForView(const GURL& url,
                                             int render_process_id,
-                                            int render_view_id) const {
+                                            int render_view_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderViewKey key(render_process_id, render_view_id);
 
diff --git a/content/browser/host_zoom_map_impl.h b/content/browser/host_zoom_map_impl.h
index 3bf890a6d..b5df85c 100644
--- a/content/browser/host_zoom_map_impl.h
+++ b/content/browser/host_zoom_map_impl.h
@@ -32,11 +32,11 @@
       int render_process_id, int render_view_id) override;
   void CopyFrom(HostZoomMap* copy) override;
   double GetZoomLevelForHostAndScheme(const std::string& scheme,
-                                      const std::string& host) const override;
+                                      const std::string& host) override;
   // TODO(wjmaclean) Should we use a GURL here? crbug.com/384486
   bool HasZoomLevel(const std::string& scheme,
-                    const std::string& host) const override;
-  ZoomLevelVector GetAllZoomLevels() const override;
+                    const std::string& host) override;
+  ZoomLevelVector GetAllZoomLevels() override;
   void SetZoomLevelForHost(const std::string& host, double level) override;
   void InitializeZoomLevelForHost(const std::string& host,
                                   double level,
@@ -45,21 +45,21 @@
                                     const std::string& host,
                                     double level) override;
   bool UsesTemporaryZoomLevel(int render_process_id,
-                              int render_view_id) const override;
+                              int render_view_id) override;
   void SetTemporaryZoomLevel(int render_process_id,
                              int render_view_id,
                              double level) override;
   void ClearZoomLevels(base::Time delete_begin, base::Time delete_end) override;
   void ClearTemporaryZoomLevel(int render_process_id,
                                int render_view_id) override;
-  double GetDefaultZoomLevel() const override;
+  double GetDefaultZoomLevel() override;
   void SetDefaultZoomLevel(double level) override;
   std::unique_ptr<Subscription> AddZoomLevelChangedCallback(
       const ZoomLevelChangedCallback& callback) override;
 
   // Returns the current zoom level for the specified WebContents. This may
   // be a temporary zoom level, depending on UsesTemporaryZoomLevel().
-  double GetZoomLevelForWebContents(WebContentsImpl* web_contents_impl) const;
+  double GetZoomLevelForWebContents(WebContentsImpl* web_contents_impl);
 
   bool PageScaleFactorIsOneForWebContents(
       WebContentsImpl* web_contents_impl) const;
@@ -87,7 +87,7 @@
   // scheme+host-keyed.
   double GetZoomLevelForView(const GURL& url,
                              int render_process_id,
-                             int render_view_id) const;
+                             int render_view_id);
 
   void SendErrorPageZoomLevelRefresh();
 
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 4af78754..635be69 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -431,7 +431,7 @@
 }
 
 base::FilePath IndexedDBContextImpl::GetFilePathForTesting(
-    const Origin& origin) const {
+    const Origin& origin) {
   return GetLevelDBPath(origin);
 }
 
@@ -626,7 +626,7 @@
   return origin_set_.get();
 }
 
-base::SequencedTaskRunner* IndexedDBContextImpl::TaskRunner() const {
+base::SequencedTaskRunner* IndexedDBContextImpl::TaskRunner() {
   DCHECK(task_runner_.get());
   return task_runner_.get();
 }
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h
index 0a6117b..cac327b 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.h
+++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -86,13 +86,12 @@
   int64_t GetOriginDiskUsage(const url::Origin& origin);
 
   // IndexedDBContext implementation:
-  base::SequencedTaskRunner* TaskRunner() const override;
+  base::SequencedTaskRunner* TaskRunner() override;
   std::vector<StorageUsageInfo> GetAllOriginsInfo() override;
   void DeleteForOrigin(const url::Origin& origin) override;
   void CopyOriginData(const url::Origin& origin,
                       IndexedDBContext* dest_context) override;
-  base::FilePath GetFilePathForTesting(
-      const url::Origin& origin) const override;
+  base::FilePath GetFilePathForTesting(const url::Origin& origin) override;
   void ResetCachesForTesting() override;
   void SetForceKeepSessionState() override;
 
diff --git a/content/browser/memory/swap_metrics_driver_impl.cc b/content/browser/memory/swap_metrics_driver_impl.cc
index f16e904..849dd38c 100644
--- a/content/browser/memory/swap_metrics_driver_impl.cc
+++ b/content/browser/memory/swap_metrics_driver_impl.cc
@@ -38,7 +38,7 @@
   return result;
 }
 
-bool SwapMetricsDriverImpl::IsRunning() const {
+bool SwapMetricsDriverImpl::IsRunning() {
   return timer_.IsRunning();
 }
 
diff --git a/content/browser/memory/swap_metrics_driver_impl.h b/content/browser/memory/swap_metrics_driver_impl.h
index 8339110c..b9c406a 100644
--- a/content/browser/memory/swap_metrics_driver_impl.h
+++ b/content/browser/memory/swap_metrics_driver_impl.h
@@ -24,7 +24,7 @@
 
   // SwapMetricsDriver
   SwapMetricsDriver::SwapMetricsUpdateResult InitializeMetrics() override;
-  bool IsRunning() const override;
+  bool IsRunning() override;
   SwapMetricsDriver::SwapMetricsUpdateResult Start() override;
   void Stop() override;
   SwapMetricsDriver::SwapMetricsUpdateResult UpdateMetrics() override;
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc
index 1693e74..076a684a 100644
--- a/content/browser/network_service_browsertest.cc
+++ b/content/browser/network_service_browsertest.cc
@@ -105,7 +105,7 @@
   TestWebUIDataSource() {}
   ~TestWebUIDataSource() override {}
 
-  std::string GetSource() const override { return "webui"; }
+  std::string GetSource() override { return "webui"; }
 
   void StartDataRequest(
       const std::string& path,
@@ -117,7 +117,7 @@
     callback.Run(response.get());
   }
 
-  std::string GetMimeType(const std::string& path) const override {
+  std::string GetMimeType(const std::string& path) override {
     return "text/html";
   }
 
diff --git a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
index b2603154..92fbc9c6 100644
--- a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "build/build_config.h"
 #include "content/common/media/media_player_delegate_messages.h"
 #include "content/public/browser/overlay_window.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -124,7 +125,14 @@
   PictureInPictureServiceImpl* service_impl_;
 };
 
-TEST_F(PictureInPictureServiceImplTest, EnterPictureInPicture) {
+// Flaky on Android. https://crbug.com/970866
+#if defined(OS_ANDROID)
+#define MAYBE_EnterPictureInPicture DISABLED_EnterPictureInPicture
+#else
+#define MAYBE_EnterPictureInPicture EnterPictureInPicture
+#endif
+
+TEST_F(PictureInPictureServiceImplTest, MAYBE_EnterPictureInPicture) {
   const int kPlayerVideoOnlyId = 30;
 
   DummyPictureInPictureSessionObserver observer;
diff --git a/content/browser/presentation/presentation_service_impl.cc b/content/browser/presentation/presentation_service_impl.cc
index d9dc94b..e4c81a6ce 100644
--- a/content/browser/presentation/presentation_service_impl.cc
+++ b/content/browser/presentation/presentation_service_impl.cc
@@ -514,7 +514,7 @@
     ~ScreenAvailabilityListenerImpl() = default;
 
 GURL PresentationServiceImpl::ScreenAvailabilityListenerImpl::
-    GetAvailabilityUrl() const {
+    GetAvailabilityUrl() {
   return availability_url_;
 }
 
diff --git a/content/browser/presentation/presentation_service_impl.h b/content/browser/presentation/presentation_service_impl.h
index 58356e8..2306ba95 100644
--- a/content/browser/presentation/presentation_service_impl.h
+++ b/content/browser/presentation/presentation_service_impl.h
@@ -117,7 +117,7 @@
     ~ScreenAvailabilityListenerImpl() override;
 
     // PresentationScreenAvailabilityListener implementation.
-    GURL GetAvailabilityUrl() const override;
+    GURL GetAvailabilityUrl() override;
     void OnScreenAvailabilityChanged(
         blink::mojom::ScreenAvailability availability) override;
 
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
index 42bdfd8..07a5b71 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/metrics/metrics_hashes.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "build/build_config.h"
 #include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/test_rappor_service.h"
 #include "components/ukm/test_ukm_recorder.h"
@@ -212,7 +213,16 @@
       "Event.Latency.ScrollBegin.Wheel.GpuSwap2", 0, 1);
 }
 
-TEST_F(RenderWidgetHostLatencyTrackerTest, TestWheelToFirstScrollHistograms) {
+// Flaky on Android. https://crbug.com/970841
+#if defined(OS_ANDROID)
+#define MAYBE_TestWheelToFirstScrollHistograms \
+  DISABLED_TestWheelToFirstScrollHistograms
+#else
+#define MAYBE_TestWheelToFirstScrollHistograms TestWheelToFirstScrollHistograms
+#endif
+
+TEST_F(RenderWidgetHostLatencyTrackerTest,
+       MAYBE_TestWheelToFirstScrollHistograms) {
   const GURL url(kUrl);
   size_t total_ukm_entry_count = 0;
   contents()->NavigateAndCommit(url);
@@ -318,7 +328,14 @@
   }
 }
 
-TEST_F(RenderWidgetHostLatencyTrackerTest, TestWheelToScrollHistograms) {
+// Flaky on Android. https://crbug.com/970841
+#if defined(OS_ANDROID)
+#define MAYBE_TestWheelToScrollHistograms DISABLED_TestWheelToScrollHistograms
+#else
+#define MAYBE_TestWheelToScrollHistograms TestWheelToScrollHistograms
+#endif
+
+TEST_F(RenderWidgetHostLatencyTrackerTest, MAYBE_TestWheelToScrollHistograms) {
   const GURL url(kUrl);
   size_t total_ukm_entry_count = 0;
   contents()->NavigateAndCommit(url);
@@ -424,7 +441,16 @@
   }
 }
 
-TEST_F(RenderWidgetHostLatencyTrackerTest, TestInertialToScrollHistograms) {
+// Flaky on Android. https://crbug.com/970841
+#if defined(OS_ANDROID)
+#define MAYBE_TestInertialToScrollHistograms \
+  DISABLED_TestInertialToScrollHistograms
+#else
+#define MAYBE_TestInertialToScrollHistograms TestInertialToScrollHistograms
+#endif
+
+TEST_F(RenderWidgetHostLatencyTrackerTest,
+       MAYBE_TestInertialToScrollHistograms) {
   const GURL url(kUrl);
   contents()->NavigateAndCommit(url);
   for (bool rendering_on_main : {false, true}) {
@@ -475,7 +501,16 @@
   }
 }
 
-TEST_F(RenderWidgetHostLatencyTrackerTest, TestTouchToFirstScrollHistograms) {
+// Flaky on Android. https://crbug.com/970841
+#if defined(OS_ANDROID)
+#define MAYBE_TestTouchToFirstScrollHistograms \
+  DISABLED_TestTouchToFirstScrollHistograms
+#else
+#define MAYBE_TestTouchToFirstScrollHistograms TestTouchToFirstScrollHistograms
+#endif
+
+TEST_F(RenderWidgetHostLatencyTrackerTest,
+       MAYBE_TestTouchToFirstScrollHistograms) {
   const GURL url(kUrl);
   contents()->NavigateAndCommit(url);
   size_t total_ukm_entry_count = 0;
@@ -585,7 +620,14 @@
   }
 }
 
-TEST_F(RenderWidgetHostLatencyTrackerTest, TestTouchToScrollHistograms) {
+// Flaky on Android. https://crbug.com/970841
+#if defined(OS_ANDROID)
+#define MAYBE_TestTouchToScrollHistograms DISABLED_TestTouchToScrollHistograms
+#else
+#define MAYBE_TestTouchToScrollHistograms TestTouchToScrollHistograms
+#endif
+
+TEST_F(RenderWidgetHostLatencyTrackerTest, MAYBE_TestTouchToScrollHistograms) {
   const GURL url(kUrl);
   contents()->NavigateAndCommit(url);
   size_t total_ukm_entry_count = 0;
@@ -692,7 +734,14 @@
   }
 }
 
-TEST_F(RenderWidgetHostLatencyTrackerTest, ScrollbarEndToEndHistograms) {
+// Flaky on Android. https://crbug.com/970841
+#if defined(OS_ANDROID)
+#define MAYBE_ScrollbarEndToEndHistograms DISABLED_ScrollbarEndToEndHistograms
+#else
+#define MAYBE_ScrollbarEndToEndHistograms ScrollbarEndToEndHistograms
+#endif
+
+TEST_F(RenderWidgetHostLatencyTrackerTest, MAYBE_ScrollbarEndToEndHistograms) {
   // For all combinations of ScrollBegin/ScrollUpdate main/impl rendering,
   // ensure that the LatencyTracker logs the correct set of histograms.
   const GURL url(kUrl);
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.cc b/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
index 760bb72..5c3dbee 100644
--- a/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
+++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
@@ -53,7 +53,7 @@
 }
 
 const cc::RenderFrameMetadata&
-RenderFrameMetadataProviderImpl::LastRenderFrameMetadata() const {
+RenderFrameMetadataProviderImpl::LastRenderFrameMetadata() {
   return last_render_frame_metadata_;
 }
 
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.h b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
index 2ec032d..5e0ddf9f 100644
--- a/content/browser/renderer_host/render_frame_metadata_provider_impl.h
+++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
@@ -42,7 +42,7 @@
   // submissions.
   void ReportAllFrameSubmissionsForTesting(bool enabled) override;
 
-  const cc::RenderFrameMetadata& LastRenderFrameMetadata() const override;
+  const cc::RenderFrameMetadata& LastRenderFrameMetadata() override;
 
  private:
   friend class FakeRenderWidgetHostViewAura;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index bad90d7..50d6f67 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1548,7 +1548,7 @@
   return bluetooth_connected_device_count_ > 0;
 }
 
-bool WebContentsImpl::IsConnectedToSerialPort() const {
+bool WebContentsImpl::IsConnectedToSerialPort() {
   return serial_active_frame_count_ > 0;
 }
 
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index f08208b..415d4a1 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -344,7 +344,7 @@
   void SetAudioMuted(bool mute) override;
   bool IsCurrentlyAudible() override;
   bool IsConnectedToBluetoothDevice() override;
-  bool IsConnectedToSerialPort() const override;
+  bool IsConnectedToSerialPort() override;
   bool HasPictureInPictureVideo() override;
   bool IsCrashed() override;
   void SetIsCrashed(base::TerminationStatus status, int error_code) override;
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc
index f45c8ec..d68f328 100644
--- a/content/browser/webauth/webauth_browsertest.cc
+++ b/content/browser/webauth/webauth_browsertest.cc
@@ -95,7 +95,7 @@
     "webauth: SecurityError: 'rp.icon' should be a secure URL";
 
 constexpr char kAbortErrorMessage[] =
-    "webauth: AbortError: The user aborted a request.";
+    "webauth: AbortError: Request has been aborted.";
 
 // Templates to be used with base::ReplaceStringPlaceholders. Can be
 // modified to include up to 9 replacements. The default values for
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index cce9341..3030e949 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -273,7 +273,7 @@
 SharedResourcesDataSource::~SharedResourcesDataSource() {
 }
 
-std::string SharedResourcesDataSource::GetSource() const {
+std::string SharedResourcesDataSource::GetSource() {
   return kChromeUIResourcesHost;
 }
 
@@ -314,14 +314,13 @@
   callback.Run(bytes.get());
 }
 
-bool SharedResourcesDataSource::AllowCaching() const {
+bool SharedResourcesDataSource::AllowCaching() {
   // Should not be cached to reflect dynamically-generated contents that may
   // depend on the current locale.
   return false;
 }
 
-std::string SharedResourcesDataSource::GetMimeType(
-    const std::string& path) const {
+std::string SharedResourcesDataSource::GetMimeType(const std::string& path) {
   if (path.empty())
     return "text/html";
 
@@ -361,13 +360,12 @@
   return "text/plain";
 }
 
-bool SharedResourcesDataSource::ShouldServeMimeTypeAsContentTypeHeader() const {
+bool SharedResourcesDataSource::ShouldServeMimeTypeAsContentTypeHeader() {
   return true;
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
-SharedResourcesDataSource::TaskRunnerForRequestPath(
-    const std::string& path) const {
+SharedResourcesDataSource::TaskRunnerForRequestPath(const std::string& path) {
   // Since WebContentsGetter can only be run on the UI thread, always return
   // a task runner if we need to choose between Polymer resources based on the
   // WebContents that is requesting the resource.
@@ -388,9 +386,8 @@
   return nullptr;
 }
 
-std::string
-SharedResourcesDataSource::GetAccessControlAllowOriginForOrigin(
-    const std::string& origin) const {
+std::string SharedResourcesDataSource::GetAccessControlAllowOriginForOrigin(
+    const std::string& origin) {
   // For now we give access only for "chrome://*" origins.
   // According to CORS spec, Access-Control-Allow-Origin header doesn't support
   // wildcards, so we need to set its value explicitly by passing the |origin|
@@ -404,7 +401,7 @@
   return origin;
 }
 
-bool SharedResourcesDataSource::IsGzipped(const std::string& path) const {
+bool SharedResourcesDataSource::IsGzipped(const std::string& path) {
   return GetContentClient()->IsDataResourceGzipped(GetIdrForPath(path));
 }
 
diff --git a/content/browser/webui/shared_resources_data_source.h b/content/browser/webui/shared_resources_data_source.h
index 4e114cf..36b6a63 100644
--- a/content/browser/webui/shared_resources_data_source.h
+++ b/content/browser/webui/shared_resources_data_source.h
@@ -20,19 +20,19 @@
   SharedResourcesDataSource();
 
   // URLDataSource implementation.
-  std::string GetSource() const override;
+  std::string GetSource() override;
   void StartDataRequest(
       const std::string& path,
       const ResourceRequestInfo::WebContentsGetter& wc_getter,
       const URLDataSource::GotDataCallback& callback) override;
-  bool AllowCaching() const override;
-  std::string GetMimeType(const std::string& path) const override;
-  bool ShouldServeMimeTypeAsContentTypeHeader() const override;
+  bool AllowCaching() override;
+  std::string GetMimeType(const std::string& path) override;
+  bool ShouldServeMimeTypeAsContentTypeHeader() override;
   scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerForRequestPath(
-      const std::string& path) const override;
+      const std::string& path) override;
   std::string GetAccessControlAllowOriginForOrigin(
-      const std::string& origin) const override;
-  bool IsGzipped(const std::string& path) const override;
+      const std::string& origin) override;
+  bool IsGzipped(const std::string& path) override;
 #if defined(OS_CHROMEOS)
   void DisablePolymer2ForHost(const std::string& host) override;
 #endif  // defined (OS_CHROMEOS)
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc
index 789bb881..84a12355 100644
--- a/content/browser/webui/web_ui_data_source_impl.cc
+++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -63,8 +63,8 @@
   ~InternalDataSource() override {}
 
   // URLDataSource implementation.
-  std::string GetSource() const override { return parent_->GetSource(); }
-  std::string GetMimeType(const std::string& path) const override {
+  std::string GetSource() override { return parent_->GetSource(); }
+  std::string GetMimeType(const std::string& path) override {
     return parent_->GetMimeType(path);
   }
   void StartDataRequest(
@@ -73,33 +73,31 @@
       const URLDataSource::GotDataCallback& callback) override {
     return parent_->StartDataRequest(path, wc_getter, callback);
   }
-  bool ShouldReplaceExistingSource() const override {
+  bool ShouldReplaceExistingSource() override {
     return parent_->replace_existing_source_;
   }
-  bool AllowCaching() const override { return false; }
-  bool ShouldAddContentSecurityPolicy() const override {
-    return parent_->add_csp_;
-  }
-  std::string GetContentSecurityPolicyScriptSrc() const override {
+  bool AllowCaching() override { return false; }
+  bool ShouldAddContentSecurityPolicy() override { return parent_->add_csp_; }
+  std::string GetContentSecurityPolicyScriptSrc() override {
     if (parent_->script_src_set_)
       return parent_->script_src_;
     return URLDataSource::GetContentSecurityPolicyScriptSrc();
   }
-  std::string GetContentSecurityPolicyObjectSrc() const override {
+  std::string GetContentSecurityPolicyObjectSrc() override {
     if (parent_->object_src_set_)
       return parent_->object_src_;
     return URLDataSource::GetContentSecurityPolicyObjectSrc();
   }
-  std::string GetContentSecurityPolicyChildSrc() const override {
+  std::string GetContentSecurityPolicyChildSrc() override {
     if (parent_->frame_src_set_)
       return parent_->frame_src_;
     return URLDataSource::GetContentSecurityPolicyChildSrc();
   }
-  bool ShouldDenyXFrameOptions() const override {
+  bool ShouldDenyXFrameOptions() override {
     return parent_->deny_xframe_options_;
   }
-  bool ShouldServeMimeTypeAsContentTypeHeader() const override { return true; }
-  bool IsGzipped(const std::string& path) const override {
+  bool ShouldServeMimeTypeAsContentTypeHeader() override { return true; }
+  bool IsGzipped(const std::string& path) override {
     return parent_->IsGzipped(path);
   }
 
@@ -235,7 +233,7 @@
   AddLocalizedStrings(defaults);
 }
 
-std::string WebUIDataSourceImpl::GetSource() const {
+std::string WebUIDataSourceImpl::GetSource() {
   return source_name_;
 }
 
diff --git a/content/browser/webui/web_ui_data_source_impl.h b/content/browser/webui/web_ui_data_source_impl.h
index 9de2d81..3d09b66 100644
--- a/content/browser/webui/web_ui_data_source_impl.h
+++ b/content/browser/webui/web_ui_data_source_impl.h
@@ -50,7 +50,7 @@
   void OverrideContentSecurityPolicyObjectSrc(const std::string& data) override;
   void OverrideContentSecurityPolicyChildSrc(const std::string& data) override;
   void DisableDenyXFrameOptions() override;
-  std::string GetSource() const override;
+  std::string GetSource() override;
 
   // URLDataSourceImpl:
   const ui::TemplateReplacements* GetReplacements() const override;
diff --git a/content/browser/webui/web_ui_impl.cc b/content/browser/webui/web_ui_impl.cc
index 8ebddd2e..a6d18fa 100644
--- a/content/browser/webui/web_ui_impl.cc
+++ b/content/browser/webui/web_ui_impl.cc
@@ -150,15 +150,15 @@
   DisallowJavascriptOnAllHandlers();
 }
 
-WebContents* WebUIImpl::GetWebContents() const {
+WebContents* WebUIImpl::GetWebContents() {
   return web_contents_;
 }
 
-float WebUIImpl::GetDeviceScaleFactor() const {
+float WebUIImpl::GetDeviceScaleFactor() {
   return GetScaleFactorForView(web_contents_->GetRenderWidgetHostView());
 }
 
-const base::string16& WebUIImpl::GetOverriddenTitle() const {
+const base::string16& WebUIImpl::GetOverriddenTitle() {
   return overridden_title_;
 }
 
@@ -166,7 +166,7 @@
   overridden_title_ = title;
 }
 
-int WebUIImpl::GetBindings() const {
+int WebUIImpl::GetBindings() {
   return bindings_;
 }
 
@@ -174,7 +174,7 @@
   bindings_ = bindings;
 }
 
-WebUIController* WebUIImpl::GetController() const {
+WebUIController* WebUIImpl::GetController() {
   return controller_.get();
 }
 
diff --git a/content/browser/webui/web_ui_impl.h b/content/browser/webui/web_ui_impl.h
index a0e36cec..2bd6dbe 100644
--- a/content/browser/webui/web_ui_impl.h
+++ b/content/browser/webui/web_ui_impl.h
@@ -43,13 +43,13 @@
   void RenderFrameHostSwappingOut();
 
   // WebUI implementation:
-  WebContents* GetWebContents() const override;
-  WebUIController* GetController() const override;
+  WebContents* GetWebContents() override;
+  WebUIController* GetController() override;
   void SetController(std::unique_ptr<WebUIController> controller) override;
-  float GetDeviceScaleFactor() const override;
-  const base::string16& GetOverriddenTitle() const override;
+  float GetDeviceScaleFactor() override;
+  const base::string16& GetOverriddenTitle() override;
   void OverrideTitle(const base::string16& title) override;
-  int GetBindings() const override;
+  int GetBindings() override;
   void SetBindings(int bindings) override;
   void AddMessageHandler(std::unique_ptr<WebUIMessageHandler> handler) override;
   void RegisterMessageCallback(base::StringPiece message,
diff --git a/content/common/user_agent.cc b/content/common/user_agent.cc
index f37818e..65083a0 100644
--- a/content/common/user_agent.cc
+++ b/content/common/user_agent.cc
@@ -16,7 +16,7 @@
 
 #if defined(OS_WIN)
 #include "base/win/windows_version.h"
-#elif (defined(OS_POSIX) && !defined(OS_MACOSX)) || defined(OS_FUCHSIA)
+#elif defined(OS_POSIX) && !defined(OS_MACOSX)
 #include <sys/utsname.h>
 #endif
 
@@ -30,17 +30,16 @@
 #endif  // defined(OS_ANDROID)
 
 std::string GetUserAgentPlatform() {
-  return
-#if defined(OS_WIN)
-      "";
+#if defined(OS_WIN) || defined(OS_FUCHSIA)
+  return "";
 #elif defined(OS_MACOSX)
-      "Macintosh; ";
+  return "Macintosh; ";
 #elif defined(USE_X11) || defined(USE_OZONE)
-      "X11; ";  // strange, but that's what Firefox uses
+  return "X11; ";  // strange, but that's what Firefox uses
 #elif defined(OS_ANDROID)
-      "Linux; ";
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-      "Unknown; ";
+  return "Linux; ";
+#elif defined(OS_POSIX)
+  return "Unknown; ";
 #endif
 }
 
@@ -86,7 +85,7 @@
 #elif defined(OS_ANDROID)
   std::string android_version_str = base::SysInfo::OperatingSystemVersion();
   std::string android_info_str = GetAndroidOSInfo(include_android_build_number);
-#elif (defined(OS_POSIX) && !defined(OS_MACOSX)) || defined(OS_FUCHSIA)
+#elif defined(OS_POSIX) && !defined(OS_MACOSX)
   // Should work on any Posix system.
   struct utsname unixinfo;
   uname(&unixinfo);
@@ -94,42 +93,36 @@
   std::string cputype;
   // special case for biarch systems
   if (strcmp(unixinfo.machine, "x86_64") == 0 &&
-      sizeof(void*) == sizeof(int32_t)) {  // NOLINT
+      sizeof(void*) == sizeof(int32_t)) {
     cputype.assign("i686 (x86_64)");
   } else {
     cputype.assign(unixinfo.machine);
   }
 #endif
 
-  base::StringAppendF(
-      &os_cpu,
+  base::StringAppendF(&os_cpu,
 #if defined(OS_WIN)
-      "Windows NT %d.%d%s",
-      os_major_version,
-      os_minor_version,
-      architecture_token.c_str()
+                      "Windows NT %d.%d%s", os_major_version, os_minor_version,
+                      architecture_token.c_str()
 #elif defined(OS_MACOSX)
-      "Intel Mac OS X %d_%d_%d",
-      os_major_version,
-      os_minor_version,
-      os_bugfix_version
+                      "Intel Mac OS X %d_%d_%d", os_major_version,
+                      os_minor_version, os_bugfix_version
 #elif defined(OS_CHROMEOS)
-      "CrOS "
-      "%s %d.%d.%d",
-      cputype.c_str(),   // e.g. i686
-      os_major_version,
-      os_minor_version,
-      os_bugfix_version
+                      "CrOS "
+                      "%s %d.%d.%d",
+                      cputype.c_str(),  // e.g. i686
+                      os_major_version, os_minor_version, os_bugfix_version
 #elif defined(OS_ANDROID)
-      "Android %s%s",
-      android_version_str.c_str(),
-      android_info_str.c_str()
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+                      "Android %s%s", android_version_str.c_str(),
+                      android_info_str.c_str()
+#elif defined(OS_FUCHSIA)
+                      "Fuchsia"
+#elif defined(OS_POSIX)
                       "%s %s",
                       unixinfo.sysname,  // e.g. Linux
                       cputype.c_str()    // e.g. i686
 #endif
-  );  // NOLINT
+  );
 
   return os_cpu;
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/OWNERS b/content/public/android/java/src/org/chromium/content/browser/selection/OWNERS
index 4943acc..395fd997 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/OWNERS
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/OWNERS
@@ -1 +1 @@
-amaralp@chromium.org
+ctzsm@chromium.org
diff --git a/content/public/android/junit/src/org/chromium/content/browser/selection/OWNERS b/content/public/android/junit/src/org/chromium/content/browser/selection/OWNERS
index 4943acc..395fd997 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/selection/OWNERS
+++ b/content/public/android/junit/src/org/chromium/content/browser/selection/OWNERS
@@ -1 +1 @@
-amaralp@chromium.org
+ctzsm@chromium.org
diff --git a/content/public/browser/gpu_data_manager.h b/content/public/browser/gpu_data_manager.h
index 46dec9d..76a2d18 100644
--- a/content/public/browser/gpu_data_manager.h
+++ b/content/public/browser/gpu_data_manager.h
@@ -42,7 +42,7 @@
   // This is only called by extensions testing.
   virtual void BlacklistWebGLForTesting() = 0;
 
-  virtual gpu::GPUInfo GetGPUInfo() const = 0;
+  virtual gpu::GPUInfo GetGPUInfo() = 0;
 
   // This indicator might change because we could collect more GPU info or
   // because the GPU blacklist could be updated.
@@ -51,17 +51,17 @@
   // Can be called on any thread.
   // If |reason| is not nullptr and GPU access is blocked, upon return, |reason|
   // contains a description of the reason why GPU access is blocked.
-  virtual bool GpuAccessAllowed(std::string* reason) const = 0;
+  virtual bool GpuAccessAllowed(std::string* reason) = 0;
 
   // Requests complete GPU info if it has not already been requested
   virtual void RequestCompleteGpuInfoIfNeeded() = 0;
 
   // Check if basic and context GPU info have been collected.
-  virtual bool IsEssentialGpuInfoAvailable() const = 0;
+  virtual bool IsEssentialGpuInfoAvailable() = 0;
 
   // Requests that the GPU process report its current video memory usage stats.
   virtual void RequestVideoMemoryUsageStatsUpdate(
-      VideoMemoryUsageStatsCallback callback) const = 0;
+      VideoMemoryUsageStatsCallback callback) = 0;
 
   // Registers/unregister |observer|.
   virtual void AddObserver(GpuDataManagerObserver* observer) = 0;
@@ -70,11 +70,11 @@
   virtual void DisableHardwareAcceleration() = 0;
 
   // Whether a GPU is in use (as opposed to a software renderer).
-  virtual bool HardwareAccelerationEnabled() const = 0;
+  virtual bool HardwareAccelerationEnabled() = 0;
 
   // Insert switches into gpu process command line: kUseGL, etc.
   virtual void AppendGpuCommandLine(base::CommandLine* command_line,
-                                    GpuProcessKind kind) const = 0;
+                                    GpuProcessKind kind) = 0;
 
  protected:
   virtual ~GpuDataManager() {}
diff --git a/content/public/browser/host_zoom_map.h b/content/public/browser/host_zoom_map.h
index 50ee4fb..1335258b 100644
--- a/content/public/browser/host_zoom_map.h
+++ b/content/public/browser/host_zoom_map.h
@@ -112,19 +112,18 @@
   // description for details.
   //
   // This may be called on any thread.
-  virtual double GetZoomLevelForHostAndScheme(
-      const std::string& scheme,
-      const std::string& host) const = 0;
+  virtual double GetZoomLevelForHostAndScheme(const std::string& scheme,
+                                              const std::string& host) = 0;
 
   // Returns true if the specified |scheme| and/or |host| has a zoom level
   // currently set.
   //
   // This may be called on any thread.
   virtual bool HasZoomLevel(const std::string& scheme,
-                            const std::string& host) const = 0;
+                            const std::string& host) = 0;
 
   // Returns all non-temporary zoom levels. Can be called on any thread.
-  virtual ZoomLevelVector GetAllZoomLevels() const = 0;
+  virtual ZoomLevelVector GetAllZoomLevels() = 0;
 
   // Here |host| is the host portion of URL, or (in the absence of a host)
   // the complete spec of the URL.
@@ -156,7 +155,7 @@
   // Returns whether the view manages its zoom level independently of other
   // views displaying content from the same host.
   virtual bool UsesTemporaryZoomLevel(int render_process_id,
-                                      int render_view_id) const = 0;
+                                      int render_view_id) = 0;
 
   // Sets the temporary zoom level that's only valid for the lifetime of this
   // WebContents.
@@ -179,7 +178,7 @@
                                        int render_view_id) = 0;
 
   // Get/Set the default zoom level for pages that don't override it.
-  virtual double GetDefaultZoomLevel() const = 0;
+  virtual double GetDefaultZoomLevel() = 0;
   virtual void SetDefaultZoomLevel(double level) = 0;
 
   typedef base::Callback<void(const ZoomLevelChange&)> ZoomLevelChangedCallback;
diff --git a/content/public/browser/indexed_db_context.h b/content/public/browser/indexed_db_context.h
index 36bc0d3..9a271d1f 100644
--- a/content/public/browser/indexed_db_context.h
+++ b/content/public/browser/indexed_db_context.h
@@ -33,7 +33,7 @@
 class IndexedDBContext : public base::RefCountedThreadSafe<IndexedDBContext> {
  public:
   // Only call the below methods by posting to this TaskRunner.
-  virtual base::SequencedTaskRunner* TaskRunner() const = 0;
+  virtual base::SequencedTaskRunner* TaskRunner() = 0;
 
   // Methods used in response to QuotaManager requests.
   virtual std::vector<StorageUsageInfo> GetAllOriginsInfo() = 0;
@@ -47,8 +47,7 @@
                               IndexedDBContext* dest_context) = 0;
 
   // Get the file name of the local storage file for the given origin.
-  virtual base::FilePath GetFilePathForTesting(
-      const url::Origin& origin) const = 0;
+  virtual base::FilePath GetFilePathForTesting(const url::Origin& origin) = 0;
 
   // Forget the origins/sizes read from disk.
   virtual void ResetCachesForTesting() = 0;
diff --git a/content/public/browser/navigation_controller.cc b/content/public/browser/navigation_controller.cc
index 28abe11..7b11ca8f 100644
--- a/content/public/browser/navigation_controller.cc
+++ b/content/public/browser/navigation_controller.cc
@@ -25,6 +25,7 @@
       should_clear_history_list(false),
       started_from_context_menu(false),
       navigation_ui_data(nullptr),
+      from_download_cross_origin_redirect(false),
       was_activated(WasActivatedOption::kUnknown),
       reload_type(ReloadType::NONE) {}
 
diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h
index 5211573a..da0c06a9 100644
--- a/content/public/browser/navigation_controller.h
+++ b/content/public/browser/navigation_controller.h
@@ -201,6 +201,10 @@
     // ContentBrowserClient::GetNavigationUIData.
     std::unique_ptr<NavigationUIData> navigation_ui_data;
 
+    // Whether this navigation was triggered by a x-origin redirect following a
+    // prior (most likely <a download>) download attempt.
+    bool from_download_cross_origin_redirect;
+
     // Time at which the input leading to this navigation occurred. This field
     // is set for links clicked by the user; the embedder is recommended to set
     // it for navigations it initiates.
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index bb0a5bc..7436eb4 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -343,6 +343,10 @@
 
   // Returns whether this navigation is currently deferred.
   virtual bool IsDeferredForTesting() = 0;
+
+  // Whether this navigation was triggered by a x-origin redirect following a
+  // prior (most likely <a download>) download attempt.
+  virtual bool FromDownloadCrossOriginRedirect() = 0;
 };
 
 }  // namespace content
diff --git a/content/public/browser/presentation_screen_availability_listener.h b/content/public/browser/presentation_screen_availability_listener.h
index 09ab473..eaf51967 100644
--- a/content/public/browser/presentation_screen_availability_listener.h
+++ b/content/public/browser/presentation_screen_availability_listener.h
@@ -23,7 +23,7 @@
   // Returns the screen availability URL associated with this listener.
   // Empty string means this object is listening for screen availability
   // for "1-UA" mode, i.e. offscreen tab rendering.
-  virtual GURL GetAvailabilityUrl() const = 0;
+  virtual GURL GetAvailabilityUrl() = 0;
 
   // Called when screen availability for the associated Presentation URL has
   // changed to |availability|.
diff --git a/content/public/browser/push_messaging_service.h b/content/public/browser/push_messaging_service.h
index 7b2a07a..9f361a6 100644
--- a/content/public/browser/push_messaging_service.h
+++ b/content/public/browser/push_messaging_service.h
@@ -51,7 +51,7 @@
   // Returns the absolute URL to the endpoint of the push service where messages
   // should be posted to. Should return an endpoint compatible with the Web Push
   // Protocol when |standard_protocol| is true.
-  virtual GURL GetEndpoint(bool standard_protocol) const = 0;
+  virtual GURL GetEndpoint(bool standard_protocol) = 0;
 
   // Subscribe the given |options.sender_info| with the push messaging service
   // in a document context. The frame is known and a permission UI may be
diff --git a/content/public/browser/render_frame_metadata_provider.h b/content/public/browser/render_frame_metadata_provider.h
index 3072f9c..590bdf2 100644
--- a/content/public/browser/render_frame_metadata_provider.h
+++ b/content/public/browser/render_frame_metadata_provider.h
@@ -52,7 +52,7 @@
   // submissions.
   virtual void ReportAllFrameSubmissionsForTesting(bool enabled) = 0;
 
-  virtual const cc::RenderFrameMetadata& LastRenderFrameMetadata() const = 0;
+  virtual const cc::RenderFrameMetadata& LastRenderFrameMetadata() = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(RenderFrameMetadataProvider);
diff --git a/content/public/browser/ssl_host_state_delegate.h b/content/public/browser/ssl_host_state_delegate.h
index 9d8f1c2..de25f85 100644
--- a/content/public/browser/ssl_host_state_delegate.h
+++ b/content/public/browser/ssl_host_state_delegate.h
@@ -66,10 +66,9 @@
 
   // Returns whether the specified host ran insecure content of the given
   // |content_type|.
-  virtual bool DidHostRunInsecureContent(
-      const std::string& host,
-      int child_id,
-      InsecureContentType content_type) const = 0;
+  virtual bool DidHostRunInsecureContent(const std::string& host,
+                                         int child_id,
+                                         InsecureContentType content_type) = 0;
 
   // Revokes all SSL certificate error allow exceptions made by the user for
   // |host|.
@@ -79,7 +78,7 @@
   // |host|. This does not mean that *all* certificate errors are allowed, just
   // that there exists an exception. To see if a particular certificate and
   // error combination exception is allowed, use QueryPolicy().
-  virtual bool HasAllowException(const std::string& host) const = 0;
+  virtual bool HasAllowException(const std::string& host) = 0;
 
  protected:
   virtual ~SSLHostStateDelegate() {}
diff --git a/content/public/browser/swap_metrics_driver.h b/content/public/browser/swap_metrics_driver.h
index 58684fa2..382f8fdc 100644
--- a/content/public/browser/swap_metrics_driver.h
+++ b/content/public/browser/swap_metrics_driver.h
@@ -84,7 +84,7 @@
   // This method must be called from the same sequence that Start() is called
   // on. If Start() has not yet been called, the subsequent call to Start() must
   // be from the same sequence.
-  virtual bool IsRunning() const = 0;
+  virtual bool IsRunning() = 0;
 
   // Starts computing swap metrics every at the interval specified in the
   // constructor, which must be > 0. If an error occurs while initializing the
diff --git a/content/public/browser/url_data_source.cc b/content/public/browser/url_data_source.cc
index 31edfa0..14a367a 100644
--- a/content/public/browser/url_data_source.cc
+++ b/content/public/browser/url_data_source.cc
@@ -53,64 +53,64 @@
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
-URLDataSource::TaskRunnerForRequestPath(const std::string& path) const {
+URLDataSource::TaskRunnerForRequestPath(const std::string& path) {
   return base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI});
 }
 
-bool URLDataSource::ShouldReplaceExistingSource() const {
+bool URLDataSource::ShouldReplaceExistingSource() {
   return true;
 }
 
-bool URLDataSource::AllowCaching() const {
+bool URLDataSource::AllowCaching() {
   return true;
 }
 
-bool URLDataSource::ShouldAddContentSecurityPolicy() const {
+bool URLDataSource::ShouldAddContentSecurityPolicy() {
   return true;
 }
 
-std::string URLDataSource::GetContentSecurityPolicyScriptSrc() const {
+std::string URLDataSource::GetContentSecurityPolicyScriptSrc() {
   // Note: Do not add 'unsafe-eval' here. Instead override CSP for the
   // specific pages that need it, see context http://crbug.com/525224.
   return "script-src chrome://resources 'self';";
 }
 
-std::string URLDataSource::GetContentSecurityPolicyObjectSrc() const {
+std::string URLDataSource::GetContentSecurityPolicyObjectSrc() {
   return "object-src 'none';";
 }
 
-std::string URLDataSource::GetContentSecurityPolicyChildSrc() const {
+std::string URLDataSource::GetContentSecurityPolicyChildSrc() {
   return "child-src 'none';";
 }
 
-std::string URLDataSource::GetContentSecurityPolicyStyleSrc() const {
+std::string URLDataSource::GetContentSecurityPolicyStyleSrc() {
   return std::string();
 }
 
-std::string URLDataSource::GetContentSecurityPolicyImgSrc() const {
+std::string URLDataSource::GetContentSecurityPolicyImgSrc() {
   return std::string();
 }
 
-bool URLDataSource::ShouldDenyXFrameOptions() const {
+bool URLDataSource::ShouldDenyXFrameOptions() {
   return true;
 }
 
 bool URLDataSource::ShouldServiceRequest(const GURL& url,
                                          ResourceContext* resource_context,
-                                         int render_process_id) const {
+                                         int render_process_id) {
   return url.SchemeIs(kChromeDevToolsScheme) || url.SchemeIs(kChromeUIScheme);
 }
 
-bool URLDataSource::ShouldServeMimeTypeAsContentTypeHeader() const {
+bool URLDataSource::ShouldServeMimeTypeAsContentTypeHeader() {
   return false;
 }
 
 std::string URLDataSource::GetAccessControlAllowOriginForOrigin(
-    const std::string& origin) const {
+    const std::string& origin) {
   return std::string();
 }
 
-bool URLDataSource::IsGzipped(const std::string& path) const {
+bool URLDataSource::IsGzipped(const std::string& path) {
   return false;
 }
 
diff --git a/content/public/browser/url_data_source.h b/content/public/browser/url_data_source.h
index 4fc97fb5..9ccdddfa 100644
--- a/content/public/browser/url_data_source.h
+++ b/content/public/browser/url_data_source.h
@@ -52,7 +52,7 @@
   // identifier, the suffix "://" must be added to the return value, eg. for a
   // URLDataSource which would display resources with URLs on the form
   // your-scheme://anything , GetSource() must return "your-scheme://".
-  virtual std::string GetSource() const = 0;
+  virtual std::string GetSource() = 0;
 
   // Used by StartDataRequest so that the child class can return the data when
   // it's available.
@@ -78,7 +78,7 @@
 
   // Return the mimetype that should be sent with this response, or empty
   // string to specify no mime type.
-  virtual std::string GetMimeType(const std::string& path) const = 0;
+  virtual std::string GetMimeType(const std::string& path) = 0;
 
   // Returns the TaskRunner on which the delegate wishes to have
   // StartDataRequest called to handle the request for |path|. The default
@@ -89,7 +89,7 @@
   // requests more rapidly when there is a large amount of UI thread contention.
   // Or the delegate can return a specific thread's TaskRunner if they wish.
   virtual scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerForRequestPath(
-      const std::string& path) const;
+      const std::string& path);
 
   // Returns true if the URLDataSource should replace an existing URLDataSource
   // with the same name that has already been registered. The default is true.
@@ -97,10 +97,10 @@
   // WARNING: this is invoked on the IO thread.
   //
   // TODO: nuke this and convert all callers to not replace.
-  virtual bool ShouldReplaceExistingSource() const;
+  virtual bool ShouldReplaceExistingSource();
 
   // Returns true if responses from this URLDataSource can be cached.
-  virtual bool AllowCaching() const;
+  virtual bool AllowCaching();
 
   // If you are overriding the following two methods, then you have a bug.
   // It is not acceptable to disable content-security-policy on chrome:// pages
@@ -109,30 +109,30 @@
   // compliant with the policy. This typically involves ensuring that all script
   // is delivered through the data manager backend. Do not disable CSP on your
   // page without first contacting the chrome security team.
-  virtual bool ShouldAddContentSecurityPolicy() const;
+  virtual bool ShouldAddContentSecurityPolicy();
   // For pre-existing code, enabling CSP with relaxed script-src attributes
   // may be marginally better than disabling CSP outright.
   // Do not override this method without first contacting the chrome security
   // team.
   // By default, "script-src chrome://resources 'self';" is added to CSP.
   // Override to change this.
-  virtual std::string GetContentSecurityPolicyScriptSrc() const;
+  virtual std::string GetContentSecurityPolicyScriptSrc();
 
   // It is OK to override the following methods to a custom CSP directive
   // thereby slightly reducing the protection applied to the page.
 
   // By default, "object-src 'none';" is added to CSP. Override to change this.
-  virtual std::string GetContentSecurityPolicyObjectSrc() const;
+  virtual std::string GetContentSecurityPolicyObjectSrc();
   // By default, "child-src 'none';" is added to CSP. Override to change this.
-  virtual std::string GetContentSecurityPolicyChildSrc() const;
+  virtual std::string GetContentSecurityPolicyChildSrc();
   // By default empty. Override to change this.
-  virtual std::string GetContentSecurityPolicyStyleSrc() const;
+  virtual std::string GetContentSecurityPolicyStyleSrc();
   // By default empty. Override to change this.
-  virtual std::string GetContentSecurityPolicyImgSrc() const;
+  virtual std::string GetContentSecurityPolicyImgSrc();
 
   // By default, the "X-Frame-Options: DENY" header is sent. To stop this from
   // happening, return false. It is OK to return false as needed.
-  virtual bool ShouldDenyXFrameOptions() const;
+  virtual bool ShouldDenyXFrameOptions();
 
   // By default, only chrome: and devtools: requests are allowed.
   // Override in specific WebUI data sources to enable for additional schemes or
@@ -141,14 +141,14 @@
   // WebUI scheme support for an embedder.
   virtual bool ShouldServiceRequest(const GURL& url,
                                     ResourceContext* resource_context,
-                                    int render_process_id) const;
+                                    int render_process_id);
 
   // By default, Content-Type: header is not sent along with the response.
   // To start sending mime type returned by GetMimeType in HTTP headers,
   // return true. It is useful when tunneling response served from this data
   // source programmatically. Or when AppCache is enabled for this source as it
   // is for devtools.
-  virtual bool ShouldServeMimeTypeAsContentTypeHeader() const;
+  virtual bool ShouldServeMimeTypeAsContentTypeHeader();
 
   // This method is called when the request contains "Origin:" header. The value
   // of the header is passed in |origin| parameter. If the returned value is not
@@ -157,10 +157,10 @@
   // |origin|, or "*", or "none", or empty string.
   // Default implementation returns an empty string.
   virtual std::string GetAccessControlAllowOriginForOrigin(
-      const std::string& origin) const;
+      const std::string& origin);
 
   // Whether |path| is gzipped (and should be transmitted gzipped).
-  virtual bool IsGzipped(const std::string& path) const;
+  virtual bool IsGzipped(const std::string& path);
 
   // Called on the UI thread. For the shared resource, disables using Polymer 2
   // for requests from |host|, even if WebUIPolymer2 is enabled. Assumes this
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 6cf2ca2..dd69a8ac 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -476,7 +476,7 @@
 
   // Indicates whether any frame in the WebContents is connected to a serial
   // port.
-  virtual bool IsConnectedToSerialPort() const = 0;
+  virtual bool IsConnectedToSerialPort() = 0;
 
   // Indicates whether a video is in Picture-in-Picture for |this|.
   virtual bool HasPictureInPictureVideo() = 0;
diff --git a/content/public/browser/web_ui.h b/content/public/browser/web_ui.h
index 1c46a1a..759056d 100644
--- a/content/public/browser/web_ui.h
+++ b/content/public/browser/web_ui.h
@@ -48,25 +48,25 @@
 
   virtual ~WebUI() {}
 
-  virtual WebContents* GetWebContents() const = 0;
+  virtual WebContents* GetWebContents() = 0;
 
-  virtual WebUIController* GetController() const = 0;
+  virtual WebUIController* GetController() = 0;
   virtual void SetController(std::unique_ptr<WebUIController> controller) = 0;
 
   // Returns the device scale factor of the monitor that the renderer is on.
   // Whenever possible, WebUI should push resources with this scale factor to
   // Javascript.
-  virtual float GetDeviceScaleFactor() const = 0;
+  virtual float GetDeviceScaleFactor() = 0;
 
   // Gets a custom tab title provided by the Web UI. If there is no title
   // override, the string will be empty which should trigger the default title
   // behavior for the tab.
-  virtual const base::string16& GetOverriddenTitle() const = 0;
+  virtual const base::string16& GetOverriddenTitle() = 0;
   virtual void OverrideTitle(const base::string16& title) = 0;
 
   // Allows a controller to override the BindingsPolicy that should be enabled
   // for this page.
-  virtual int GetBindings() const = 0;
+  virtual int GetBindings() = 0;
   virtual void SetBindings(int bindings) = 0;
 
   virtual void AddMessageHandler(
diff --git a/content/public/browser/web_ui_data_source.h b/content/public/browser/web_ui_data_source.h
index 7a1973a..d200317 100644
--- a/content/public/browser/web_ui_data_source.h
+++ b/content/public/browser/web_ui_data_source.h
@@ -119,7 +119,7 @@
   virtual void DisableDenyXFrameOptions() = 0;
 
   // The |source_name| this WebUIDataSource was created with.
-  virtual std::string GetSource() const = 0;
+  virtual std::string GetSource() = 0;
 };
 
 }  // namespace content
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index e02af24c..554a7c4 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -97,6 +97,7 @@
   MOCK_METHOD0(IsDeferredForTesting, bool());
   MOCK_METHOD1(RegisterSubresourceOverride,
                void(mojom::TransferrableURLLoaderPtr));
+  MOCK_METHOD0(FromDownloadCrossOriginRedirect, bool());
   MOCK_METHOD0(IsSameProcess, bool());
   MOCK_METHOD0(GetNavigationEntryOffset, int());
 
diff --git a/content/public/test/test_web_ui.cc b/content/public/test/test_web_ui.cc
index a591c5a..5184e71 100644
--- a/content/public/test/test_web_ui.cc
+++ b/content/public/test/test_web_ui.cc
@@ -40,11 +40,11 @@
     callback.Run(args);
 }
 
-WebContents* TestWebUI::GetWebContents() const {
+WebContents* TestWebUI::GetWebContents() {
   return web_contents_;
 }
 
-WebUIController* TestWebUI::GetController() const {
+WebUIController* TestWebUI::GetController() {
   return controller_.get();
 }
 
@@ -52,15 +52,15 @@
   controller_ = std::move(controller);
 }
 
-float TestWebUI::GetDeviceScaleFactor() const {
+float TestWebUI::GetDeviceScaleFactor() {
   return 1.0f;
 }
 
-const base::string16& TestWebUI::GetOverriddenTitle() const {
+const base::string16& TestWebUI::GetOverriddenTitle() {
   return temp_string_;
 }
 
-int TestWebUI::GetBindings() const {
+int TestWebUI::GetBindings() {
   return bindings_;
 }
 
diff --git a/content/public/test/test_web_ui.h b/content/public/test/test_web_ui.h
index 68c30218..0e40ae2 100644
--- a/content/public/test/test_web_ui.h
+++ b/content/public/test/test_web_ui.h
@@ -29,13 +29,13 @@
   }
 
   // WebUI overrides.
-  WebContents* GetWebContents() const override;
-  WebUIController* GetController() const override;
+  WebContents* GetWebContents() override;
+  WebUIController* GetController() override;
   void SetController(std::unique_ptr<WebUIController> controller) override;
-  float GetDeviceScaleFactor() const override;
-  const base::string16& GetOverriddenTitle() const override;
+  float GetDeviceScaleFactor() override;
+  const base::string16& GetOverriddenTitle() override;
   void OverrideTitle(const base::string16& title) override {}
-  int GetBindings() const override;
+  int GetBindings() override;
   void SetBindings(int bindings) override;
   void AddMessageHandler(std::unique_ptr<WebUIMessageHandler> handler) override;
   void RegisterMessageCallback(base::StringPiece message,
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 33817ba2..f46d82f 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -3362,10 +3362,12 @@
   using ReportTimeCallback = blink::WebWidgetClient::ReportTimeCallback;
 
  public:
-  ReportTimeSwapPromise(ReportTimeCallback callback,
+  ReportTimeSwapPromise(ReportTimeCallback swap_time_callback,
+                        ReportTimeCallback presentation_time_callback,
                         scoped_refptr<base::SingleThreadTaskRunner> task_runner,
                         base::WeakPtr<RenderWidget> render_widget)
-      : callback_(std::move(callback)),
+      : swap_time_callback_(std::move(swap_time_callback)),
+        presentation_time_callback_(std::move(presentation_time_callback)),
         task_runner_(std::move(task_runner)),
         render_widget_(std::move(render_widget)) {}
   ~ReportTimeSwapPromise() override = default;
@@ -3381,21 +3383,10 @@
   void DidSwap() override {
     DCHECK_GT(frame_token_, 0u);
     task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            [](base::TimeTicks timestamp, ReportTimeCallback callback,
-               base::WeakPtr<RenderWidget> render_widget, int frame_token) {
-              std::move(callback).Run(
-                  blink::WebWidgetClient::SwapResult::kDidSwap, timestamp);
-              if (render_widget) {
-                render_widget->layer_tree_view()->AddPresentationCallback(
-                    frame_token,
-                    base::BindOnce(&RecordSwapTimeToPresentationTime,
-                                   timestamp));
-              }
-            },
-            base::TimeTicks::Now(), std::move(callback_), render_widget_,
-            frame_token_));
+        FROM_HERE, base::BindOnce(&RunCallbackAfterSwap, base::TimeTicks::Now(),
+                                  std::move(swap_time_callback_),
+                                  std::move(presentation_time_callback_),
+                                  std::move(render_widget_), frame_token_));
   }
 
   cc::SwapPromise::DidNotSwapAction DidNotSwap(
@@ -3419,14 +3410,45 @@
     // using presentation or swap timestamps.
     task_runner_->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(callback_), result, base::TimeTicks::Now()));
+        base::BindOnce(
+            [](blink::WebWidgetClient::SwapResult result,
+               base::TimeTicks swap_time, ReportTimeCallback swap_time_callback,
+               ReportTimeCallback presentation_time_callback) {
+              ReportTime(std::move(swap_time_callback), result, swap_time);
+              ReportTime(std::move(presentation_time_callback), result,
+                         swap_time);
+            },
+            result, base::TimeTicks::Now(), std::move(swap_time_callback_),
+            std::move(presentation_time_callback_)));
     return DidNotSwapAction::BREAK_PROMISE;
   }
 
   int64_t TraceId() const override { return 0; }
 
  private:
-  static void RecordSwapTimeToPresentationTime(
+  static void RunCallbackAfterSwap(
+      base::TimeTicks swap_time,
+      ReportTimeCallback swap_time_callback,
+      ReportTimeCallback presentation_time_callback,
+      base::WeakPtr<RenderWidget> render_widget,
+      int frame_token) {
+    if (render_widget) {
+      render_widget->layer_tree_view()->AddPresentationCallback(
+          frame_token,
+          base::BindOnce(&RunCallbackAfterPresentation,
+                         std::move(presentation_time_callback), swap_time));
+      ReportTime(std::move(swap_time_callback),
+                 blink::WebWidgetClient::SwapResult::kDidSwap, swap_time);
+    } else {
+      ReportTime(std::move(swap_time_callback),
+                 blink::WebWidgetClient::SwapResult::kDidSwap, swap_time);
+      ReportTime(std::move(presentation_time_callback),
+                 blink::WebWidgetClient::SwapResult::kDidSwap, swap_time);
+    }
+  }
+
+  static void RunCallbackAfterPresentation(
+      ReportTimeCallback presentation_time_callback,
       base::TimeTicks swap_time,
       base::TimeTicks presentation_time) {
     DCHECK(!swap_time.is_null());
@@ -3440,9 +3462,20 @@
           "PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime",
           presentation_time - swap_time);
     }
+    ReportTime(std::move(presentation_time_callback),
+               blink::WebWidgetClient::SwapResult::kDidSwap,
+               presentation_time_is_valid ? presentation_time : swap_time);
   }
 
-  ReportTimeCallback callback_;
+  static void ReportTime(ReportTimeCallback callback,
+                         blink::WebWidgetClient::SwapResult result,
+                         base::TimeTicks time) {
+    if (callback)
+      std::move(callback).Run(result, time);
+  }
+
+  ReportTimeCallback swap_time_callback_;
+  ReportTimeCallback presentation_time_callback_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   base::WeakPtr<RenderWidget> render_widget_;
   uint32_t frame_token_ = 0;
@@ -3451,11 +3484,17 @@
 };
 
 void RenderWidget::NotifySwapTime(ReportTimeCallback callback) {
+  NotifySwapAndPresentationTime(base::NullCallback(), std::move(callback));
+}
+
+void RenderWidget::NotifySwapAndPresentationTime(
+    ReportTimeCallback swap_time_callback,
+    ReportTimeCallback presentation_time_callback) {
   cc::LayerTreeHost* layer_tree_host = layer_tree_view_->layer_tree_host();
   // When the WebWidget is closed we cancel any pending SwapPromise that would
   // call back into blink, so we use |close_weak_ptr_factory_|.
   layer_tree_host->QueueSwapPromise(std::make_unique<ReportTimeSwapPromise>(
-      std::move(callback),
+      std::move(swap_time_callback), std::move(presentation_time_callback),
       layer_tree_host->GetTaskRunnerProvider()->MainThreadTaskRunner(),
       close_weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 1115b02..47e137f 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -460,6 +460,16 @@
                      base::OnceCallback<void(bool)> callback) override;
   void NotifySwapTime(ReportTimeCallback callback) override;
 
+  // Registers a SwapPromise to report presentation time and possibly swap time.
+  // If |swap_time_callback| is not a null callback, it would be called once
+  // swap happens. |presentation_time_callback| will be called some time after
+  // pixels are presented on screen. Swap time is needed only in tests and
+  // production code uses |NotifySwapTime()| above which calls this one passing
+  // a null callback as |swap_time_callback|.
+  void NotifySwapAndPresentationTime(
+      ReportTimeCallback swap_time_callback,
+      ReportTimeCallback presentation_time_callback);
+
   // Override point to obtain that the current input method state and caret
   // position.
   ui::TextInputType GetTextInputType();
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index 1b7cf5d..ff458c4 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -727,35 +727,56 @@
     color_layer->SetBackgroundColor(SK_ColorRED);
   }
 
-  base::TimeTicks CompositeAndReturnSwapTimestamp() {
+  // |swap_to_presentation| determines how long after swap should presentation
+  // happen. This can be negative, positive, or zero. If zero, an invalid (null)
+  // presentation time is used.
+  void CompositeAndWaitForPresentation(base::TimeDelta swap_to_presentation) {
+    base::RunLoop swap_run_loop;
+    base::RunLoop presentation_run_loop;
+
+    // Register callbacks for swap time and presentation time.
     base::TimeTicks swap_time;
-    base::RunLoop run_loop;
-    widget()->NotifySwapTime(base::BindOnce(
-        [](base::OnceClosure callback, base::TimeTicks* swap_time,
-           blink::WebWidgetClient::SwapResult result,
-           base::TimeTicks timestamp) {
-          *swap_time = timestamp;
-          std::move(callback).Run();
-        },
-        run_loop.QuitClosure(), &swap_time));
+    widget()->NotifySwapAndPresentationTime(
+        base::BindOnce(
+            [](base::OnceClosure swap_quit_closure, base::TimeTicks* swap_time,
+               blink::WebWidgetClient::SwapResult result,
+               base::TimeTicks timestamp) {
+              DCHECK(!timestamp.is_null());
+              *swap_time = timestamp;
+              std::move(swap_quit_closure).Run();
+            },
+            swap_run_loop.QuitClosure(), &swap_time),
+        base::BindOnce(
+            [](base::OnceClosure presentation_quit_closure,
+               blink::WebWidgetClient::SwapResult result,
+               base::TimeTicks timestamp) {
+              DCHECK(!timestamp.is_null());
+              std::move(presentation_quit_closure).Run();
+            },
+            presentation_run_loop.QuitClosure()));
+
+    // Composite and wait for the swap to complete.
     widget()->layer_tree_view()->layer_tree_host()->Composite(
-        base::TimeTicks::Now(), /*raster=*/true);
-    // The swap time notify comes as a posted task.
-    run_loop.Run();
-    return swap_time;
+        base::TimeTicks::Now(),
+        /*raster=*/true);
+    swap_run_loop.Run();
+
+    // Present and wait for it to complete.
+    base::TimeTicks presentation_time;
+    if (!swap_to_presentation.is_zero())
+      presentation_time = swap_time + swap_to_presentation;
+    widget()->layer_tree_view()->DidPresentCompositorFrame(
+        1, gfx::PresentationFeedback(presentation_time,
+                                     base::TimeDelta::FromMilliseconds(16), 0));
+    presentation_run_loop.Run();
   }
 };
 
 TEST_F(NotifySwapTimesRenderWidgetUnittest, PresentationTimestampValid) {
   base::HistogramTester histograms;
 
-  base::TimeTicks swap_time = CompositeAndReturnSwapTimestamp();
-  ASSERT_FALSE(swap_time.is_null());
+  CompositeAndWaitForPresentation(base::TimeDelta::FromMilliseconds(2));
 
-  widget()->layer_tree_view()->DidPresentCompositorFrame(
-      1, gfx::PresentationFeedback(
-             swap_time + base::TimeDelta::FromMilliseconds(2),
-             base::TimeDelta::FromMilliseconds(16), 0));
   EXPECT_THAT(histograms.GetAllSamples(
                   "PageLoad.Internal.Renderer.PresentationTime.Valid"),
               testing::ElementsAre(base::Bucket(true, 1)));
@@ -768,11 +789,8 @@
 TEST_F(NotifySwapTimesRenderWidgetUnittest, PresentationTimestampInvalid) {
   base::HistogramTester histograms;
 
-  base::TimeTicks swap_time = CompositeAndReturnSwapTimestamp();
-  ASSERT_FALSE(swap_time.is_null());
+  CompositeAndWaitForPresentation(base::TimeDelta());
 
-  widget()->layer_tree_view()->DidPresentCompositorFrame(
-      1, gfx::PresentationFeedback());
   EXPECT_THAT(histograms.GetAllSamples(
                   "PageLoad.Internal.Renderer.PresentationTime.Valid"),
               testing::ElementsAre(base::Bucket(false, 1)));
@@ -786,13 +804,8 @@
        PresentationTimestampEarlierThanSwaptime) {
   base::HistogramTester histograms;
 
-  base::TimeTicks swap_time = CompositeAndReturnSwapTimestamp();
-  ASSERT_FALSE(swap_time.is_null());
+  CompositeAndWaitForPresentation(base::TimeDelta::FromMilliseconds(-2));
 
-  widget()->layer_tree_view()->DidPresentCompositorFrame(
-      1, gfx::PresentationFeedback(
-             swap_time - base::TimeDelta::FromMilliseconds(2),
-             base::TimeDelta::FromMilliseconds(16), 0));
   EXPECT_THAT(histograms.GetAllSamples(
                   "PageLoad.Internal.Renderer.PresentationTime.Valid"),
               testing::ElementsAre(base::Bucket(false, 1)));
diff --git a/content/shell/browser/web_test/web_test_push_messaging_service.cc b/content/shell/browser/web_test/web_test_push_messaging_service.cc
index 210c950f..528f625 100644
--- a/content/shell/browser/web_test/web_test_push_messaging_service.cc
+++ b/content/shell/browser/web_test/web_test_push_messaging_service.cc
@@ -49,7 +49,7 @@
 
 WebTestPushMessagingService::~WebTestPushMessagingService() {}
 
-GURL WebTestPushMessagingService::GetEndpoint(bool standard_protocol) const {
+GURL WebTestPushMessagingService::GetEndpoint(bool standard_protocol) {
   return GURL(standard_protocol ? "https://example.com/StandardizedEndpoint/"
                                 : "https://example.com/LayoutTestEndpoint/");
 }
diff --git a/content/shell/browser/web_test/web_test_push_messaging_service.h b/content/shell/browser/web_test/web_test_push_messaging_service.h
index fb36035f..b2a10fd 100644
--- a/content/shell/browser/web_test/web_test_push_messaging_service.h
+++ b/content/shell/browser/web_test/web_test_push_messaging_service.h
@@ -25,7 +25,7 @@
   ~WebTestPushMessagingService() override;
 
   // PushMessagingService implementation:
-  GURL GetEndpoint(bool standard_protocol) const override;
+  GURL GetEndpoint(bool standard_protocol) override;
   void SubscribeFromDocument(const GURL& requesting_origin,
                              int64_t service_worker_registration_id,
                              int renderer_id,
diff --git a/content/test/mock_ssl_host_state_delegate.cc b/content/test/mock_ssl_host_state_delegate.cc
index 324db081..744494b 100644
--- a/content/test/mock_ssl_host_state_delegate.cc
+++ b/content/test/mock_ssl_host_state_delegate.cc
@@ -52,7 +52,7 @@
 bool MockSSLHostStateDelegate::DidHostRunInsecureContent(
     const std::string& host,
     int child_id,
-    InsecureContentType content_type) const {
+    InsecureContentType content_type) {
   return false;
 }
 
@@ -61,8 +61,7 @@
   exceptions_.erase(exceptions_.find(host));
 }
 
-bool MockSSLHostStateDelegate::HasAllowException(
-    const std::string& host) const {
+bool MockSSLHostStateDelegate::HasAllowException(const std::string& host) {
   return exceptions_.find(host) != exceptions_.end();
 }
 
diff --git a/content/test/mock_ssl_host_state_delegate.h b/content/test/mock_ssl_host_state_delegate.h
index 8205ef1..45e3efc 100644
--- a/content/test/mock_ssl_host_state_delegate.h
+++ b/content/test/mock_ssl_host_state_delegate.h
@@ -30,14 +30,13 @@
                               int child_id,
                               InsecureContentType content_type) override;
 
-  bool DidHostRunInsecureContent(
-      const std::string& host,
-      int child_id,
-      InsecureContentType content_type) const override;
+  bool DidHostRunInsecureContent(const std::string& host,
+                                 int child_id,
+                                 InsecureContentType content_type) override;
 
   void RevokeUserAllowExceptions(const std::string& host) override;
 
-  bool HasAllowException(const std::string& host) const override;
+  bool HasAllowException(const std::string& host) override;
 
  private:
   std::set<std::string> exceptions_;
diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc
index f5e9dde..78c66a00 100644
--- a/device/bluetooth/bluetooth_adapter_unittest.cc
+++ b/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -79,9 +79,6 @@
 
   bool IsDiscovering() const override { return false; }
 
-  void StartDiscoverySession(const DiscoverySessionCallback& callback,
-                             const ErrorCallback& error_callback) {}
-
   UUIDList GetUUIDs() const override { return UUIDList(); }
 
   void CreateRfcommService(
diff --git a/extensions/browser/OWNERS b/extensions/browser/OWNERS
index c21998b..1893077 100644
--- a/extensions/browser/OWNERS
+++ b/extensions/browser/OWNERS
@@ -1,7 +1,6 @@
 # Please talk to the apps shell team before adding DEPS.
 per-file DEPS=set noparent
 per-file DEPS=benwells@chromium.org
-per-file DEPS=derat@chromium.org
 per-file DEPS=rockot@google.com
 per-file DEPS=rdevlin.cronin@chromium.org
 
diff --git a/extensions/browser/api/power/OWNERS b/extensions/browser/api/power/OWNERS
deleted file mode 100644
index 3c97e54..0000000
--- a/extensions/browser/api/power/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-derat@chromium.org
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc
index 3e3c723..0820b76 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc
@@ -218,8 +218,13 @@
 }
 
 void MimeHandlerViewEmbedder::CheckSandboxFlags() {
-  if (!render_frame_host_->IsSandboxed(blink::WebSandboxFlags::kPlugins))
+  // If the FrameTreeNode is deleted while it has ownership of the ongoing
+  // NavigationRequest, DidFinishNavigation is called before FrameDeleted (see
+  // https://crbug.com/969840).
+  if (render_frame_host_ &&
+      !render_frame_host_->IsSandboxed(blink::WebSandboxFlags::kPlugins)) {
     return;
+  }
   // Notify the renderer to load an empty page instead.
   GetContainerManager()->LoadEmptyPage(resource_url_);
   GetMimeHandlerViewEmbeddersMap()->erase(frame_tree_node_id_);
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index 37b9361..2328f1f 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -91,21 +91,6 @@
 
 const uint32_t kMaxTransferCacheEntrySizeForTransferBuffer = 1024;
 
-void RecordPaintOpSize(size_t size) {
-  constexpr size_t kMinPaintOpSize = 512 * 1024;
-  constexpr size_t kMaxPaintOpSize = 16 * 1024 * 1024;
-
-  // Serialization failure, record max size.
-  if (size == 0u)
-    size = kMaxPaintOpSize;
-
-  if (size < kMinPaintOpSize)
-    return;
-
-  UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.OopRaster.PaintOpSerializationSize", size,
-                              kMinPaintOpSize, kMaxPaintOpSize, 50);
-}
-
 }  // namespace
 
 // Helper to copy data to the GPU service over the transfer cache.
@@ -258,13 +243,11 @@
       }
 
       if (!size) {
-        RecordPaintOpSize(0u);
         LOG(ERROR) << "Failed to serialize op in " << block_size << " bytes.";
         return 0u;
       }
     }
 
-    RecordPaintOpSize(size);
     DCHECK_LE(size, free_bytes_);
     DCHECK(base::CheckAdd<uint32_t>(written_bytes_, size).IsValid());
 
diff --git a/gpu/command_buffer/client/raster_interface.h b/gpu/command_buffer/client/raster_interface.h
index 74646c7..ccd2db38 100644
--- a/gpu/command_buffer/client/raster_interface.h
+++ b/gpu/command_buffer/client/raster_interface.h
@@ -57,7 +57,9 @@
                                    const gfx::ColorSpace& color_space,
                                    const GLbyte* mailbox) = 0;
 
-  static constexpr size_t kDefaultMaxOpSizeHint = 512 * 1024;
+  // Heuristic decided on UMA data. This covers 85% of the cases where we need
+  // to serialize ops > 512k.
+  static constexpr size_t kDefaultMaxOpSizeHint = 600 * 1024;
   virtual void RasterCHROMIUM(const cc::DisplayItemList* list,
                               cc::ImageProvider* provider,
                               const gfx::Size& content_size,
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h
index 8aef772..0e35bc60 100644
--- a/gpu/config/gpu_info.h
+++ b/gpu/config/gpu_info.h
@@ -80,7 +80,9 @@
   AV1PROFILE_PROFILE_MAIN,
   AV1PROFILE_PROFILE_HIGH,
   AV1PROFILE_PROFILE_PRO,
-  VIDEO_CODEC_PROFILE_MAX = AV1PROFILE_PROFILE_PRO,
+  DOLBYVISION_PROFILE8,
+  DOLBYVISION_PROFILE9,
+  VIDEO_CODEC_PROFILE_MAX = DOLBYVISION_PROFILE9,
 };
 
 // Specification of a decoding profile supported by a hardware decoder.
diff --git a/gpu/ipc/common/gpu_info.mojom b/gpu/ipc/common/gpu_info.mojom
index 954c46eb..9705ef1 100644
--- a/gpu/ipc/common/gpu_info.mojom
+++ b/gpu/ipc/common/gpu_info.mojom
@@ -52,6 +52,8 @@
   AV1PROFILE_PROFILE_MAIN,
   AV1PROFILE_PROFILE_HIGH,
   AV1PROFILE_PROFILE_PRO,
+  DOLBYVISION_PROFILE8,
+  DOLBYVISION_PROFILE9,
 };
 
 // gpu::VideoDecodeAcceleratorSupportedProfile
diff --git a/gpu/ipc/common/gpu_info_struct_traits.cc b/gpu/ipc/common/gpu_info_struct_traits.cc
index 64ade57..28d5662 100644
--- a/gpu/ipc/common/gpu_info_struct_traits.cc
+++ b/gpu/ipc/common/gpu_info_struct_traits.cc
@@ -79,6 +79,10 @@
       return gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE5;
     case gpu::VideoCodecProfile::DOLBYVISION_PROFILE7:
       return gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE7;
+    case gpu::VideoCodecProfile::DOLBYVISION_PROFILE8:
+      return gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE8;
+    case gpu::VideoCodecProfile::DOLBYVISION_PROFILE9:
+      return gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE9;
     case gpu::VideoCodecProfile::THEORAPROFILE_ANY:
       return gpu::mojom::VideoCodecProfile::THEORAPROFILE_ANY;
     case gpu::VideoCodecProfile::AV1PROFILE_PROFILE_MAIN:
@@ -169,6 +173,12 @@
     case gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE7:
       *out = gpu::VideoCodecProfile::DOLBYVISION_PROFILE7;
       return true;
+    case gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE8:
+      *out = gpu::VideoCodecProfile::DOLBYVISION_PROFILE8;
+      return true;
+    case gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE9:
+      *out = gpu::VideoCodecProfile::DOLBYVISION_PROFILE9;
+      return true;
     case gpu::mojom::VideoCodecProfile::THEORAPROFILE_ANY:
       *out = gpu::VideoCodecProfile::THEORAPROFILE_ANY;
       return true;
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index f842614..6ab0bbb 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1039,28 +1039,28 @@
 
     builders {
       name: "Android WebView L (dbg)"
-      mixins: "android-ci"
+      mixins: "android-ci-goma-rbe-prod"
       mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
     }
 
     builders {
       name: "Android WebView M (dbg)"
-      mixins: "android-ci"
+      mixins: "android-ci-goma-rbe-prod"
       mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
     }
 
     builders {
       name: "Android WebView N (dbg)"
-      mixins: "android-ci"
+      mixins: "android-ci-goma-rbe-prod"
       mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
     }
 
     builders {
       name: "Android WebView O (dbg)"
-      mixins: "android-ci"
+      mixins: "android-ci-goma-rbe-prod"
       mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
     }
@@ -1069,12 +1069,13 @@
       name: "Android WebView O NetworkService (dbg)"
       mixins: "android-fyi-ci"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
       dimensions: "os:Ubuntu-14.04"
     }
 
     builders {
       name: "Android x64 Builder (dbg)"
-      mixins: "android-ci"
+      mixins: "android-ci-goma-rbe-prod"
       mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
       execution_timeout_secs: 14400  # 4h
@@ -1082,7 +1083,7 @@
 
     builders {
       name: "Android x86 Builder (dbg)"
-      mixins: "android-ci"
+      mixins: "android-ci-goma-rbe-prod"
       mixins: "builderless"
       dimensions: "os:Ubuntu-14.04"
     }
@@ -1095,6 +1096,13 @@
     }
 
     builders {
+      name: "android-code-coverage"
+      mixins: "code-coverage"
+      mixins: "linux"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
       name: "android-cronet-arm-dbg"
       mixins: "android-ci"
       dimensions: "os:Ubuntu-14.04"
@@ -2352,6 +2360,7 @@
       dimensions: "os:Mac-10.13"
       dimensions: "cores:4"
       mixins: "fuzz-ci"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Libfuzzer Upload Linux MSan"
@@ -2374,6 +2383,7 @@
       dimensions: "os:Mac-10.13"
       dimensions: "cores:4"
       mixins: "fuzz-ci"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "WebKit Linux ASAN"
@@ -2386,6 +2396,7 @@
       dimensions: "cores:24"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "goma-rbe-prod"
       execution_timeout_secs: 14400 # 4 hours
     }
     builders {
@@ -2402,7 +2413,13 @@
       mixins: "memory-ci"
       # TODO(hinoka): Remove this after debugging.
       recipe {
-        properties_j: "$build/goma:{\"debug\": true}"
+        properties_j: <<END
+        $build/goma: {
+          "server_host": "goma.chromium.org",
+          "rpc_extra_params": "?prod",
+          "debug": true
+        }
+        END
       }
     }
     builders {
@@ -2480,6 +2497,7 @@
       name: "WebKit Mac10.13 (retina)"
       dimensions: "os:Mac-10.13"
       mixins: "mac-ci"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "Afl Upload Linux ASan"
@@ -2526,6 +2544,7 @@
       name: "Site Isolation Android"
       dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "MSAN Release (no origins)"
@@ -2614,6 +2633,7 @@
       dimensions: "os:Mac-10.13"
       dimensions: "cores:4"
       mixins: "fyi-ci"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "linux-archive-dbg"
@@ -2682,7 +2702,7 @@
       # TODO(https://crbug.com/919430) Remove the larger timeout once compile
       # times have been brought down to reasonable level
       execution_timeout_secs: 16200  # 4.5h
-      mixins: "memory-ci"
+      mixins: "memory-ci-goma-rbe-prod"
     }
     builders {
       name: "chromeos-amd64-generic-rel-goma-canary"
@@ -2815,6 +2835,7 @@
       name: "Mojo Android"
       dimensions: "os:Ubuntu-14.04"
       mixins: "fyi-ci"
+      mixins: "goma-rbe-prod"
     }
     # Goma RBE ToT/Staging/FYI
     builders {
@@ -3511,6 +3532,7 @@
     builders { mixins: "android-optional-gpu-try" name: "android_optional_gpu_tests_rel" }
     builders {
       mixins: "android-try"
+      mixins: "goma-rbe-prod"
       name: "android_unswarmed_pixel_aosp"
       dimensions: "os:Ubuntu-14.04"
     }
@@ -3904,11 +3926,15 @@
     # The 10.xx version translates to which bots will run isolated tests.
     builders { mixins: "mac-try" name: "mac_chromium_10.10" }
     builders { mixins: "mac-try" name: "mac_chromium_10.12_rel_ng" }
-    builders { mixins: "mac-try" name: "mac_chromium_10.13_rel_ng"}
+    builders {
+      mixins: "mac-try"
+      mixins: "goma-rbe-prod"
+      name: "mac_chromium_10.13_rel_ng"
+    }
     builders { mixins: "mac-try" name: "mac_chromium_archive_rel_ng" }
     builders {
       mixins: "mac-try"
-      mixins: "goma-j150"
+      mixins: "goma-rbe-prod-j150"
       name: "mac_chromium_asan_rel_ng"
     }
     builders {
@@ -4058,6 +4084,7 @@
       name: "android_compile_x86_dbg"
       dimensions: "os:Ubuntu-14.04"
       mixins: "android-try"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "linux-annotator-rel"
@@ -4084,6 +4111,7 @@
       name: "android_compile_x64_dbg"
       dimensions: "os:Ubuntu-14.04"
       mixins: "android-try"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "android_archive_rel_ng"
@@ -4101,11 +4129,13 @@
       dimensions: "os:Ubuntu-14.04"
       dimensions: "cores:32"
       mixins: "android-try"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "android_mojo"
       dimensions: "os:Ubuntu-14.04"
       mixins: "android-try"
+      mixins: "goma-rbe-prod"
     }
 
     # Blink try builders.
@@ -4138,6 +4168,7 @@
       name: "mac10.13_retina-blink-rel"
       mixins: "mac"
       mixins: "blink-try"
+      mixins: "goma-rbe-prod"
     }
     builders {
       name: "win10-blink-rel"
@@ -4181,22 +4212,40 @@
 
     # Android
 
-    builders { mixins: "linux-xenial"  name: "WebRTC Chromium Android Builder" }
-    builders { mixins: "linux-xenial"  name: "WebRTC Chromium Android Tester" }
+    builders {
+      name: "WebRTC Chromium Android Builder"
+      mixins: "linux-xenial"
+      mixins: "goma-rbe-prod"
+    }
+    builders {
+      name: "WebRTC Chromium Android Tester"
+      mixins: "linux-xenial"
+    }
 
     # Linux
 
-    builders { mixins: "linux-xenial"  name: "WebRTC Chromium Linux Builder" }
-    builders { mixins: "linux"  name: "WebRTC Chromium Linux Tester" }
+    builders {
+      name: "WebRTC Chromium Linux Builder"
+      mixins: "linux-xenial"
+      mixins: "goma-rbe-prod"
+    }
+    builders {
+      name: "WebRTC Chromium Linux Tester"
+      mixins: "linux"
+    }
 
     # Mac
 
     builders {
       name: "WebRTC Chromium Mac Builder"
       mixins: "mac"
+      mixins: "goma-rbe-prod"
       dimensions: "cores:8"
     }
-    builders { mixins: "mac"  name: "WebRTC Chromium Mac Tester" }
+    builders {
+      name: "WebRTC Chromium Mac Tester"
+      mixins: "mac"
+    }
 
     # Win
 
@@ -4234,9 +4283,9 @@
 
     # Android
 
-    builders { mixins: "linux-xenial"  name: "WebRTC Chromium FYI Android Builder" }
-    builders { mixins: "linux-xenial"  name: "WebRTC Chromium FYI Android Builder (dbg)" }
-    builders { mixins: "linux-xenial"  name: "WebRTC Chromium FYI Android Builder ARM64 (dbg)" }
+    builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Android Builder" }
+    builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Android Builder (dbg)" }
+    builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Android Builder ARM64 (dbg)" }
     builders { mixins: "linux-xenial"  name: "WebRTC Chromium FYI Android Tests (dbg) (K Nexus5)" }
     builders { mixins: "linux-xenial"  name: "WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)" }
 
@@ -4247,8 +4296,8 @@
 
     # Linux
 
-    builders { mixins: "linux-xenial"  name: "WebRTC Chromium FYI Linux Builder" }
-    builders { mixins: "linux-xenial"  name: "WebRTC Chromium FYI Linux Builder (dbg)" }
+    builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Linux Builder" }
+    builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Linux Builder (dbg)" }
     builders { mixins: "linux-xenial"  name: "WebRTC Chromium FYI Linux Tester" }
 
     # Mac
@@ -4256,11 +4305,13 @@
     builders {
       name: "WebRTC Chromium FYI Mac Builder"
       mixins: "mac"
+      mixins: "goma-rbe-prod"
       dimensions: "cores:8"
     }
     builders {
       name: "WebRTC Chromium FYI Mac Builder (dbg)"
       mixins: "mac"
+      mixins: "goma-rbe-prod"
       dimensions: "cores:8"
     }
     builders { mixins: "mac"  name: "WebRTC Chromium FYI Mac Tester" }
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 837936b..dc391cd 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -2049,6 +2049,11 @@
     category: "closure_compilation"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/android-code-coverage"
+    category: "code_coverage"
+    short_name: "and"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/linux-code-coverage"
     category: "code_coverage"
     short_name: "lnx"
@@ -2816,6 +2821,85 @@
     category: "week2b|mac"
     short_name: "bld"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Chromium Mac 10.13"
+    category: "week2c|mac"
+    short_name: "10.13"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Mac ASAN Release"
+    category: "week2c|mac|asan"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Mac ASAN Release Media"
+    category: "week2c|mac|asan"
+    short_name: "media"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Mac ASan 64 Builder"
+    category: "week2c|mac|asan"
+    short_name: "64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Mac ASan"
+    category: "week2c|mac|asan"
+    short_name: "fuzz"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/WebKit Mac10.13 (retina)"
+    category: "week2c|mac"
+    short_name: "webkit"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Android CFI"
+    category: "week2c|android"
+    short_name: "cfi"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Site Isolation Android"
+    category: "week2c|android"
+    short_name: "isolate"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Mojo Android"
+    category: "week2c|android"
+    short_name: "mojo"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Android x64 Builder (dbg)"
+    category: "week2c|android|dbg"
+    short_name: "x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Android x86 Builder (dbg)"
+    category: "week2c|android|dbg"
+    short_name: "x86"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Android WebView L (dbg)"
+    category: "week2c|android|dbg|webview"
+    short_name: "l"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Android WebView M (dbg)"
+    category: "week2c|android|dbg|webview"
+    short_name: "m"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Android WebView N (dbg)"
+    category: "week2c|android|dbg|webview"
+    short_name: "n"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Android WebView O (dbg)"
+    category: "week2c|android|dbg|webview"
+    short_name: "o"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Android WebView O NetworkService (dbg)"
+    category: "week2c|android|dbg|webview"
+    short_name: "o net"
+  }
 }
 
 consoles {
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index 6a98f4f..9ff02b0 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -299,6 +299,7 @@
   triggers: "WinMSVC64 Goma Latest Client"
   triggers: "Windows deterministic"
   triggers: "android-asan"
+  triggers: "android-code-coverage"
   triggers: "android-cronet-arm-dbg"
   triggers: "android-cronet-arm-rel"
   triggers: "android-cronet-arm64-dbg"
@@ -701,6 +702,16 @@
 }
 
 job {
+  id: "android-code-coverage"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-code-coverage"
+  }
+}
+
+job {
   id: "android-cronet-arm-dbg"
   acl_sets: "default"
   buildbucket: {
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index dbb026e7..e16029e 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -3539,10 +3539,19 @@
     [SizeClassRecorder pageLoadedWithHorizontalSizeClass:sizeClass];
   }
 
-  // If there is no first responder, try to make the webview the first
+  // If there is no first responder, try to make the webview or the NTP first
   // responder to have it answer keyboard commands (e.g. space bar to scroll).
-  if (!GetFirstResponder() && self.currentWebState)
-    [self.currentWebState->GetWebViewProxy() becomeFirstResponder];
+  if (!GetFirstResponder() && self.currentWebState) {
+    NewTabPageTabHelper* NTPHelper =
+        NewTabPageTabHelper::FromWebState(webState);
+    if (NTPHelper && NTPHelper->IsActive()) {
+      UIViewController* viewController =
+          _ntpCoordinatorsForWebStates[webState].viewController;
+      [viewController becomeFirstResponder];
+    } else {
+      [self.currentWebState->GetWebViewProxy() becomeFirstResponder];
+    }
+  }
 }
 
 #pragma mark - OmniboxPopupPresenterDelegate methods.
diff --git a/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.h b/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.h
index c2e1ce3..ebdad37 100644
--- a/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.h
+++ b/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.h
@@ -10,9 +10,6 @@
 // A button for an Infobar that contains a badge image.
 @interface InfobarBadgeButton : ExtendedTouchTargetButton
 
-// Gives the badge a dark gray background if |selected| is YES. Removes the
-// background if |selected| is NO. Will animate change if |animated| is YES.
-- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
 // Sets the badge color to blue if |active| is YES, light gray if |active| is
 // NO. Will animate change if |animated| is YES.
 - (void)setActive:(BOOL)active animated:(BOOL)animated;
diff --git a/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.mm b/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.mm
index 3801c38..5c5d610 100644
--- a/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.mm
+++ b/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.mm
@@ -15,8 +15,6 @@
 const CGFloat kButtonAnimationDuration = 0.2;
 // Edge insets of button.
 const CGFloat kButtonEdgeInset = 6;
-// White value of the button background in a selected state.
-const CGFloat kSelectedWhiteValue = 0.80;
 // Tint color of the button in an active state.
 const CGFloat kActiveTintColor = 0x1A73E8;
 // To achieve a circular corner radius, divide length of a side by 2.
@@ -50,21 +48,6 @@
       self.bounds.size.height / kCircularCornerRadiusDivisor;
 }
 
-- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
-  void (^changeBackgroundColor)() = ^{
-    self.backgroundColor =
-        selected ? [UIColor colorWithWhite:kSelectedWhiteValue alpha:1.0]
-                 : [UIColor clearColor];
-  };
-  if (animated) {
-    [UIView animateWithDuration:kButtonAnimationDuration
-                     animations:^{
-                       changeBackgroundColor();
-                     }];
-  } else {
-    changeBackgroundColor();
-  }
-}
 - (void)setActive:(BOOL)active animated:(BOOL)animated {
   void (^changeTintColor)() = ^{
     self.tintColor =
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
index aa2365c3..2df04ea 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
@@ -39,11 +39,6 @@
 // Infobar redesign.
 - (void)displayInfobarBadge:(BOOL)display type:(InfobarType)infobarType;
 
-// Notifies the consumer that the InfobarBadge select state has changed.
-// TODO(crbug.com/935804): This method is currently only being used in the
-// Infobar redesign.
-- (void)selectInfobarBadge:(BOOL)select;
-
 // Notifies the consumer that the InfobarBadge active state has changed.
 // TODO(crbug.com/935804): This method is currently only being used in the
 // Infobar redesign.
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index 32b84d13..bd12175 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -393,10 +393,6 @@
                             metricsRecorder:metricsRecorder];
 }
 
-- (void)selectInfobarBadge:(BOOL)select {
-  [self.viewController setInfobarButtonStyleSelected:select];
-}
-
 - (void)activeInfobarBadge:(BOOL)active {
   [self.viewController setInfobarButtonStyleActive:active];
 }
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
index 6fd8541..a74a7a2 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
@@ -168,7 +168,6 @@
 
 - (void)setBadgeState:(InfobarBadgeState)badgeState {
   _badgeState = badgeState;
-  [self.consumer selectInfobarBadge:_badgeState & InfobarBadgeStateSelected];
   [self.consumer activeInfobarBadge:_badgeState & InfobarBadgeStateAccepted];
 }
 
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h
index 10374c6..0a6c718f 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h
@@ -81,11 +81,6 @@
 // Infobar redesign.
 - (void)displayInfobarButton:(BOOL)display
              metricsRecorder:(InfobarMetricsRecorder*)metricsRecorder;
-// If |selected| is YES applies the selected styling to the InfobarButton, if NO
-// it removes it.
-// TODO(crbug.com/935804): This method is currently only being used in the
-// Infobar redesign.
-- (void)setInfobarButtonStyleSelected:(BOOL)selected;
 // If |active| is YES applies the active styling to the InfobarButton, if NO it
 // removes it.
 // TODO(crbug.com/935804): This method is currently only being used in the
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
index 325b0cf..11570d0 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -482,10 +482,6 @@
   [self.dispatcher displayModalInfobar];
 }
 
-- (void)setInfobarButtonStyleSelected:(BOOL)selected {
-  [self.locationBarSteadyView.leadingButton setSelected:selected animated:YES];
-}
-
 - (void)setInfobarButtonStyleActive:(BOOL)active {
   self.activeBadge = active;
   [self.locationBarSteadyView.leadingButton setActive:active animated:YES];
diff --git a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
index 2cdc4d73..b6f6da9 100644
--- a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
+++ b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
@@ -491,7 +491,7 @@
       @"There should be 1 typed URL entity");
 
   // Delete typed URL from client.
-  [ChromeEarlGrey deleteTypedURL:mockURL];
+  [ChromeEarlGrey deleteHistoryServiceTypedURL:mockURL];
 
   // Trigger sync and wait for typed URL to be deleted.
   [ChromeEarlGrey triggerSyncCycleForType:syncer::TYPED_URLS];
@@ -525,7 +525,7 @@
                                    name:mockURL.spec()
                                   count:1
                                 timeout:kSyncOperationTimeout]);
-  [ChromeEarlGrey deleteTypedURL:mockURL];
+  [ChromeEarlGrey deleteHistoryServiceTypedURL:mockURL];
 
   // Trigger sync and wait for fake server to be updated.
   [ChromeEarlGrey triggerSyncCycleForType:syncer::TYPED_URLS];
diff --git a/ios/chrome/browser/web/forms_egtest.mm b/ios/chrome/browser/web/forms_egtest.mm
index 7b33e06..d9aa8b7 100644
--- a/ios/chrome/browser/web/forms_egtest.mm
+++ b/ios/chrome/browser/web/forms_egtest.mm
@@ -280,6 +280,11 @@
 // Tests that a POST followed by tapping back to the form page and then tapping
 // forward to the result page resends data.
 - (void)testRepostFormAfterTappingBackAndForward {
+  // TODO(crbug.com/968296): Test is failing on iPad for slim nav.
+  if (IsIPadIdiom() && web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iPad.");
+  }
+
   [self setUpFormTestSimpleHttpServer];
   const GURL destinationURL = GetDestinationUrl();
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 30c7d00f..8bc3949 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -152,6 +152,9 @@
 // Adds typed URL into HistoryService.
 - (void)addHistoryServiceTypedURL:(const GURL&)URL;
 
+// Deletes typed URL from HistoryService.
+- (void)deleteHistoryServiceTypedURL:(const GURL&)URL;
+
 // Injects a bookmark with |URL| and |title| into the fake sync server.
 - (void)addFakeSyncServerBookmarkWithURL:(const GURL&)URL
                                    title:(const std::string&)title;
@@ -184,6 +187,14 @@
                                         count:(size_t)count
                                       timeout:(NSTimeInterval)timeout;
 
+// Induces a GREYAssert if |expected_present| is YES and the provided |url| is
+// not present, or vice versa.
+// TODO(crbug.com/963613): Change return type to void when
+// CHROME_EG_ASSERT_NO_ERROR is removed.
+- (NSError*)waitForTypedURL:(const GURL&)URL
+              expectPresent:(BOOL)expectPresent
+                    timeout:(NSTimeInterval)timeout;
+
 #pragma mark - Tab Utilities (EG2)
 
 // Opens a new tab and waits for the new tab animation to complete within a
@@ -376,27 +387,12 @@
 // CHROME_EG_ASSERT_NO_ERROR is removed.
 - (NSError*)waitForStaticHTMLViewNotContainingText:(NSString*)text;
 
-// Waits for a Chrome error page.
-// If the condition is not met within a timeout returns an NSError indicating
-// why the operation failed, otherwise nil.
-- (NSError*)waitForErrorPage WARN_UNUSED_RESULT;
-
 #pragma mark - Sync Utilities
 
 // Injects a bookmark into the fake sync server with |URL| and |title|.
 - (void)injectBookmarkOnFakeSyncServerWithURL:(const std::string&)URL
                                 bookmarkTitle:(const std::string&)title;
 
-// If the provided |url| is present (or not) if |expected_present|
-// is YES (or NO) returns nil, otherwise an NSError indicating why the operation
-// failed.
-- (NSError*)waitForTypedURL:(const GURL&)URL
-              expectPresent:(BOOL)expectPresent
-                    timeout:(NSTimeInterval)timeout WARN_UNUSED_RESULT;
-
-// Deletes typed URL from HistoryService.
-- (void)deleteTypedURL:(const GURL&)URL;
-
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 54f114e4..ec6c24c9 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -19,7 +19,6 @@
 #if defined(CHROME_EARL_GREY_1)
 #import <WebKit/WebKit.h>
 
-#include "components/strings/grit/components_strings.h"  // nogncheck
 #import "ios/chrome/browser/ui/static_content/static_html_view_controller.h"  // nogncheck
 #import "ios/chrome/test/app/chrome_test_util.h"                   // nogncheck
 #import "ios/chrome/test/app/history_test_util.h"                  // nogncheck
@@ -32,7 +31,6 @@
 #import "ios/web/public/test/web_view_content_test_util.h"         // nogncheck
 #import "ios/web/public/test/web_view_interaction_test_util.h"     // nogncheck
 #import "ios/web/public/web_state/web_state.h"                     // nogncheck
-#include "ui/base/l10n/l10n_util.h"                                // nogncheck
 #endif
 
 using base::test::ios::kWaitForJSCompletionTimeout;
@@ -41,7 +39,10 @@
 using base::test::ios::WaitUntilConditionOrTimeout;
 
 namespace {
-NSString* kWaitForPageToFinishLoadingError = @"Page did not finish loading";
+NSString* const kWaitForPageToFinishLoadingError =
+    @"Page did not finish loading";
+NSString* const kTypedURLError =
+    @"Error occurred during typed URL verification.";
 }
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -452,6 +453,29 @@
   [ChromeEarlGreyAppInterface addHistoryServiceTypedURL:spec];
 }
 
+- (void)deleteHistoryServiceTypedURL:(const GURL&)URL {
+  NSString* spec = base::SysUTF8ToNSString(URL.spec());
+  [ChromeEarlGreyAppInterface deleteHistoryServiceTypedURL:spec];
+}
+
+- (NSError*)waitForTypedURL:(const GURL&)URL
+              expectPresent:(BOOL)expectPresent
+                    timeout:(NSTimeInterval)timeout {
+  NSString* spec = base::SysUTF8ToNSString(URL.spec());
+  GREYCondition* waitForTypedURL =
+      [GREYCondition conditionWithName:kTypedURLError
+                                 block:^{
+                                   return [ChromeEarlGreyAppInterface
+                                            isTypedURL:spec
+                                       presentOnClient:expectPresent];
+                                 }];
+
+  bool success = [waitForTypedURL waitWithTimeout:timeout];
+  EG_TEST_HELPER_ASSERT_TRUE(success, kTypedURLError);
+
+  return nil;
+}
+
 - (void)triggerSyncCycleForType:(syncer::ModelType)type {
   [ChromeEarlGreyAppInterface triggerSyncCycleForType:type];
 }
@@ -576,12 +600,6 @@
 
 #pragma mark - Navigation Utilities
 
-- (NSError*)waitForErrorPage {
-  NSString* const kErrorPageText =
-      l10n_util::GetNSString(IDS_ERRORPAGES_HEADING_NOT_AVAILABLE);
-  return [self waitForStaticHTMLViewContainingText:kErrorPageText];
-}
-
 - (NSError*)waitForStaticHTMLViewContainingText:(NSString*)text {
   bool hasStaticView = WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
     return chrome_test_util::StaticHtmlViewContainingText(
@@ -616,33 +634,6 @@
   chrome_test_util::InjectBookmarkOnFakeSyncServer(URL, title);
 }
 
-- (NSError*)waitForTypedURL:(const GURL&)URL
-              expectPresent:(BOOL)expectPresent
-                    timeout:(NSTimeInterval)timeout {
-  __block NSError* error = nil;
-  ConditionBlock condition = ^{
-    NSError* __autoreleasing tempError = error;
-    BOOL success = chrome_test_util::IsTypedUrlPresentOnClient(
-        URL, expectPresent, &tempError);
-    error = tempError;
-    DCHECK(success || error);
-    return !!success;
-  };
-  bool success = WaitUntilConditionOrTimeout(timeout, condition);
-  if (error != nil) {
-    return nil;
-  }
-  if (!success) {
-    return testing::NSErrorWithLocalizedDescription(
-        @"Error occurred during typed URL verification.");
-  }
-  return nil;
-}
-
-- (void)deleteTypedURL:(const GURL&)URL {
-  chrome_test_util::DeleteTypedUrlFromClient(URL);
-}
-
 @end
 
 #endif  // defined(CHROME_EARL_GREY_1)
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index a3b348fb..21c99708 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -22,9 +22,9 @@
 // operation failed.
 + (NSError*)clearBrowsingHistory;
 
-// Loads |URL| in the current WebState with transition type
+// Loads the URL |spec| in the current WebState with transition type
 // ui::PAGE_TRANSITION_TYPED and returns without waiting for the page to load.
-+ (void)startLoadingURL:(NSString*)URL;
++ (void)startLoadingURL:(NSString*)spec;
 
 // If the current WebState is HTML content, will wait until the window ID is
 // injected. Returns YES if the injection is successful or if the WebState is
@@ -217,6 +217,14 @@
 // Adds typed URL into HistoryService.
 + (void)addHistoryServiceTypedURL:(NSString*)URL;
 
+// Deletes typed URL from HistoryService.
++ (void)deleteHistoryServiceTypedURL:(NSString*)URL;
+
+// If the provided URL |spec| is either present or not present in HistoryService
+// (depending on |expectPresent|), return YES. If the present status of |spec|
+// is not what is expected, or there is an error, return NO.
++ (BOOL)isTypedURL:(NSString*)spec presentOnClient:(BOOL)expectPresent;
+
 // Triggers a sync cycle for a |type|.
 + (void)triggerSyncCycleForType:(syncer::ModelType)type;
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index 89bad5c..a6407b7 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -42,8 +42,8 @@
       @"Clearing browser history timed out");
 }
 
-+ (void)startLoadingURL:(NSString*)URL {
-  chrome_test_util::LoadUrl(GURL(base::SysNSStringToUTF8(URL)));
++ (void)startLoadingURL:(NSString*)spec {
+  chrome_test_util::LoadUrl(GURL(base::SysNSStringToUTF8(spec)));
 }
 
 + (BOOL)waitForWindowIDInjectionIfNeeded {
@@ -311,6 +311,19 @@
   chrome_test_util::AddTypedURLOnClient(GURL(base::SysNSStringToUTF8(URL)));
 }
 
++ (void)deleteHistoryServiceTypedURL:(NSString*)URL {
+  chrome_test_util::DeleteTypedUrlFromClient(
+      GURL(base::SysNSStringToUTF8(URL)));
+}
+
++ (BOOL)isTypedURL:(NSString*)spec presentOnClient:(BOOL)expectPresent {
+  NSError* error = nil;
+  GURL URL(base::SysNSStringToUTF8(spec));
+  BOOL success =
+      chrome_test_util::IsTypedUrlPresentOnClient(URL, expectPresent, &error);
+  return success && !error;
+}
+
 + (void)triggerSyncCycleForType:(syncer::ModelType)type {
   chrome_test_util::TriggerSyncCycle(type);
 }
diff --git a/ios/chrome/test/earl_grey2/smoke_egtest.mm b/ios/chrome/test/earl_grey2/smoke_egtest.mm
index 1d08e64..7817dbd 100644
--- a/ios/chrome/test/earl_grey2/smoke_egtest.mm
+++ b/ios/chrome/test/earl_grey2/smoke_egtest.mm
@@ -179,4 +179,12 @@
                          actualResult);
 }
 
+// Tests typed URL converted helpers in chrome_earl_grey.h.
+- (void)testTypedURLHelpers {
+  const GURL mockURL("http://not-a-real-site.test/");
+
+  [ChromeEarlGrey addHistoryServiceTypedURL:mockURL];
+  [ChromeEarlGrey deleteHistoryServiceTypedURL:mockURL];
+}
+
 @end
diff --git a/ios/testing/earl_grey/BUILD.gn b/ios/testing/earl_grey/BUILD.gn
index 7a5367b..22cf840 100644
--- a/ios/testing/earl_grey/BUILD.gn
+++ b/ios/testing/earl_grey/BUILD.gn
@@ -15,6 +15,8 @@
   ]
 
   sources = [
+    "app_launch_manager.h",
+    "app_launch_manager.mm",
     "base_earl_grey_test_case.h",
     "base_earl_grey_test_case.mm",
     "base_eg_test_helper_impl.h",
@@ -59,6 +61,8 @@
   testonly = true
 
   sources = [
+    "app_launch_manager.h",
+    "app_launch_manager.mm",
     "base_earl_grey_test_case.h",
     "base_earl_grey_test_case.mm",
     "base_eg_test_helper_impl.h",
diff --git a/ios/testing/earl_grey/app_launch_manager.h b/ios/testing/earl_grey/app_launch_manager.h
new file mode 100644
index 0000000..a16a9d2
--- /dev/null
+++ b/ios/testing/earl_grey/app_launch_manager.h
@@ -0,0 +1,32 @@
+// Copyright 2019 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_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
+#define IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
+
+#import <Foundation/Foundation.h>
+
+// Provides control of the single application-under-test to EarlGrey 2 tests.
+@interface AppLaunchManager : NSObject
+
+// Returns the singleton instance of this class.
++ (AppLaunchManager*)sharedManager;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Makes sure the app has been started with the appropriate |arguments|.
+// In EG2, will launch the app if any of the following conditions are met:
+// * The app is not running
+// * The app is currently running with different arguments.
+// * |forceRestart| is YES
+// Otherwise, the app will be activated instead of (re)launched.
+// Will wait until app is activated or launched, and fail the test if it
+// fails to do so.
+// In EG1, this method is a no-op.
+- (void)ensureAppLaunchedWithArgs:(NSArray<NSString*>*)arguments
+                     forceRestart:(BOOL)forceRestart;
+
+@end
+
+#endif  // IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
\ No newline at end of file
diff --git a/ios/testing/earl_grey/app_launch_manager.mm b/ios/testing/earl_grey/app_launch_manager.mm
new file mode 100644
index 0000000..68a65b0
--- /dev/null
+++ b/ios/testing/earl_grey/app_launch_manager.mm
@@ -0,0 +1,70 @@
+// Copyright 2019 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/testing/earl_grey/app_launch_manager.h"
+
+#import <XCTest/XCTest.h>
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#if defined(CHROME_EARL_GREY_2)  // avoid unused function warning in EG1
+namespace {
+// Checks if two pairs of launch arguments are equivalent.
+bool LaunchArgumentsAreEqual(NSArray<NSString*>* args1,
+                             NSArray<NSString*>* args2) {
+  // isEqualToArray will only return true if both arrays are non-nil,
+  // so first check if both arrays are empty or nil
+  if (!args1.count && !args2.count) {
+    return true;
+  }
+
+  return [args1 isEqualToArray:args2];
+}
+}  // namespace
+#endif
+
+@interface AppLaunchManager ()
+@property(nonatomic) XCUIApplication* runningApplication;
+@property(nonatomic) NSArray<NSString*>* currentLaunchArgs;
+@end
+
+@implementation AppLaunchManager
+
++ (AppLaunchManager*)sharedManager {
+  static AppLaunchManager* instance = nil;
+  static dispatch_once_t guard;
+  dispatch_once(&guard, ^{
+    instance = [[AppLaunchManager alloc] initPrivate];
+  });
+  return instance;
+}
+
+- (instancetype)initPrivate {
+  self = [super init];
+  return self;
+}
+
+- (void)ensureAppLaunchedWithArgs:(NSArray<NSString*>*)arguments
+                     forceRestart:(BOOL)forceRestart {
+#if defined(CHROME_EARL_GREY_2)
+  bool appNeedsLaunching =
+      forceRestart || !self.runningApplication ||
+      !LaunchArgumentsAreEqual(arguments, self.currentLaunchArgs);
+
+  if (!appNeedsLaunching) {
+    [self.runningApplication activate];
+    return;
+  }
+
+  XCUIApplication* application = [[XCUIApplication alloc] init];
+  application.launchArguments = arguments;
+
+  [application launch];
+  self.runningApplication = application;
+  self.currentLaunchArgs = arguments;
+#endif
+}
+@end
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm
index fe496a9..cb4f8a3 100644
--- a/ios/testing/earl_grey/base_earl_grey_test_case.mm
+++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -7,6 +7,7 @@
 #import <UIKit/UIKit.h>
 #import <objc/runtime.h>
 
+#import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/coverage_utils.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 
@@ -41,11 +42,8 @@
 }
 
 - (void)launchAppForTestMethod {
-  static dispatch_once_t launchAppToken;
-  dispatch_once(&launchAppToken, ^{
-    XCUIApplication* application = [[XCUIApplication alloc] init];
-    [application launch];
-  });
+  [[AppLaunchManager sharedManager] ensureAppLaunchedWithArgs:nil
+                                                 forceRestart:false];
 }
 
 // Prevents tests inheriting from this class from putting logic in +setUp.
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
index 15415a50..a2ddfd5 100644
--- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
@@ -53,7 +53,7 @@
 
 /**
  * A wrapper of the android MediaDrm class. Each MediaDrmBridge manages multiple sessions for
- * MediaCodecAudioDecoders, and AndroidVideoDecodeAccelerators or MediaCodecVideoDecoders.
+ * MediaCodecAudioDecoders or MediaCodecVideoDecoders.
  */
 @JNINamespace("media")
 @MainDex
diff --git a/media/base/android/media_codec_loop.h b/media/base/android/media_codec_loop.h
index 4791db0..eded6bb 100644
--- a/media/base/android/media_codec_loop.h
+++ b/media/base/android/media_codec_loop.h
@@ -29,7 +29,7 @@
 // One MediaCodecLoop instance owns a single MediaCodec(Bridge) instance, and
 // drives it to perform decoding in conjunction with a MediaCodecLoop::Client.
 // The Client provides the input data and consumes the output data.  A typical
-// example is AndroidVideoDecodeAccelerator.
+// example is MediaCodecAudioDecoder.
 
 // Implementation notes.
 //
diff --git a/media/base/bitstream_buffer.h b/media/base/bitstream_buffer.h
index 8a72eceb9..343ca070 100644
--- a/media/base/bitstream_buffer.h
+++ b/media/base/bitstream_buffer.h
@@ -119,9 +119,7 @@
   size_t size_;
   off_t offset_;
 
-  // This is only set when necessary. For example, AndroidVideoDecodeAccelerator
-  // needs the timestamp because the underlying decoder may require it to
-  // determine the output order.
+  // Note: Not set by all clients.
   base::TimeDelta presentation_timestamp_;
 
   // Note that BitstreamBuffer uses the settings in Audio/VideoDecoderConfig
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc
index e6d3a5bd..a85513f 100644
--- a/media/base/key_systems.cc
+++ b/media/base/key_systems.cc
@@ -103,13 +103,14 @@
     case kCodecHEVC:
       return EME_CODEC_HEVC;
     case kCodecDolbyVision:
-      // Only profiles 0, 4, 5 and 7 are valid. Profile 0 is encoded based on
-      // AVC while profile 4, 5 and 7 are based on HEVC.
-      if (profile == DOLBYVISION_PROFILE0) {
+      // Only profiles 0, 4, 5, 7, 8, 9 are valid. Profile 0 and 9 are encoded
+      // based on AVC while profile 4, 5, 7 and 8 are based on HEVC.
+      if (profile == DOLBYVISION_PROFILE0 || profile == DOLBYVISION_PROFILE9) {
         return EME_CODEC_DOLBY_VISION_AVC;
       } else if (profile == DOLBYVISION_PROFILE4 ||
                  profile == DOLBYVISION_PROFILE5 ||
-                 profile == DOLBYVISION_PROFILE7) {
+                 profile == DOLBYVISION_PROFILE7 ||
+                 profile == DOLBYVISION_PROFILE8) {
         return EME_CODEC_DOLBY_VISION_HEVC;
       } else {
         return EME_CODEC_NONE;
diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc
index 80a26ca..5fa7bae4 100644
--- a/media/base/test_helpers.cc
+++ b/media/base/test_helpers.cc
@@ -160,7 +160,7 @@
     case kCodecHEVC:
       return HEVCPROFILE_MIN;
     case kCodecDolbyVision:
-      return DOLBYVISION_MIN;
+      return DOLBYVISION_PROFILE0;
     case kCodecAV1:
       return AV1PROFILE_MIN;
   }
diff --git a/media/base/video_codecs.cc b/media/base/video_codecs.cc
index c0f1835..5665b62 100644
--- a/media/base/video_codecs.cc
+++ b/media/base/video_codecs.cc
@@ -92,6 +92,10 @@
       return "dolby vision profile 5";
     case DOLBYVISION_PROFILE7:
       return "dolby vision profile 7";
+    case DOLBYVISION_PROFILE8:
+      return "dolby vision profile 8";
+    case DOLBYVISION_PROFILE9:
+      return "dolby vision profile 9";
     case THEORAPROFILE_ANY:
       return "theora";
     case AV1PROFILE_PROFILE_MAIN:
@@ -756,26 +760,31 @@
   // Profile string should be two digits.
   unsigned profile_id = 0;
   if (elem[1].size() != 2 || !base::StringToUint(elem[1], &profile_id) ||
-      profile_id > 7) {
+      profile_id > 9) {
     DVLOG(4) << __func__ << ": invalid format or profile_id=" << elem[1];
     return false;
   }
 
-  // Only profiles 0, 4, 5 and 7 are valid. Profile 0 is encoded based on AVC
-  // while profile 4, 5 and 7 are based on HEVC.
+  // Only profiles 0, 4, 5, 7, 8 and 9 are valid. Profile 0 and 9 are encoded
+  // based on AVC while profile 4, 5, 7 and 8 are based on HEVC.
   switch (profile_id) {
     case 0:
+    case 9:
       if (!IsDolbyVisionAVCCodecId(codec_id)) {
         DVLOG(4) << __func__
                  << ": codec id is mismatched with profile_id=" << profile_id;
         return false;
       }
-      *profile = DOLBYVISION_PROFILE0;
+      if (profile_id == 0)
+        *profile = DOLBYVISION_PROFILE0;
+      else if (profile_id == 9)
+        *profile = DOLBYVISION_PROFILE9;
       break;
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
     case 4:
     case 5:
     case 7:
+    case 8:
       if (!IsDolbyVisionHEVCCodecId(codec_id)) {
         DVLOG(4) << __func__
                  << ": codec id is mismatched with profile_id=" << profile_id;
@@ -787,6 +796,8 @@
         *profile = DOLBYVISION_PROFILE5;
       else if (profile_id == 7)
         *profile = DOLBYVISION_PROFILE7;
+      else if (profile_id == 8)
+        *profile = DOLBYVISION_PROFILE8;
       break;
 #endif
     default:
diff --git a/media/base/video_codecs.h b/media/base/video_codecs.h
index 119634b..79c5daf 100644
--- a/media/base/video_codecs.h
+++ b/media/base/video_codecs.h
@@ -79,12 +79,10 @@
   HEVCPROFILE_MAIN10 = 17,
   HEVCPROFILE_MAIN_STILL_PICTURE = 18,
   HEVCPROFILE_MAX = HEVCPROFILE_MAIN_STILL_PICTURE,
-  DOLBYVISION_MIN = 19,
-  DOLBYVISION_PROFILE0 = DOLBYVISION_MIN,
+  DOLBYVISION_PROFILE0 = 19,
   DOLBYVISION_PROFILE4 = 20,
   DOLBYVISION_PROFILE5 = 21,
   DOLBYVISION_PROFILE7 = 22,
-  DOLBYVISION_MAX = DOLBYVISION_PROFILE7,
   THEORAPROFILE_MIN = 23,
   THEORAPROFILE_ANY = THEORAPROFILE_MIN,
   THEORAPROFILE_MAX = THEORAPROFILE_ANY,
@@ -93,7 +91,9 @@
   AV1PROFILE_PROFILE_HIGH = 25,
   AV1PROFILE_PROFILE_PRO = 26,
   AV1PROFILE_MAX = AV1PROFILE_PROFILE_PRO,
-  VIDEO_CODEC_PROFILE_MAX = AV1PROFILE_PROFILE_PRO,
+  DOLBYVISION_PROFILE8 = 27,
+  DOLBYVISION_PROFILE9 = 28,
+  VIDEO_CODEC_PROFILE_MAX = DOLBYVISION_PROFILE9,
 };
 
 struct CodecProfileLevel {
diff --git a/media/base/video_decoder_config.cc b/media/base/video_decoder_config.cc
index 23aa265e..c16ce85 100644
--- a/media/base/video_decoder_config.cc
+++ b/media/base/video_decoder_config.cc
@@ -46,6 +46,8 @@
     case DOLBYVISION_PROFILE4:
     case DOLBYVISION_PROFILE5:
     case DOLBYVISION_PROFILE7:
+    case DOLBYVISION_PROFILE8:
+    case DOLBYVISION_PROFILE9:
       return kCodecDolbyVision;
     case THEORAPROFILE_ANY:
       return kCodecTheora;
diff --git a/media/cdm/BUILD.gn b/media/cdm/BUILD.gn
index 091ca56..d49e488 100644
--- a/media/cdm/BUILD.gn
+++ b/media/cdm/BUILD.gn
@@ -106,31 +106,6 @@
   }
 }
 
-config("cdm_manager_implementation") {
-  defines = [ "CDM_MANAGER_IMPLEMENTATION" ]
-}
-
-# cdm_manager must not be a source_set() because CdmManager exposes a static
-# singleton, shared by multiple component()s.
-#
-# TODO(xhwang): Remove this component once AVDA no longer depends on it.
-component("cdm_manager") {
-  visibility = [
-    "//media/gpu",
-    "//media/mojo/services",
-  ]
-  sources = [
-    "cdm_manager.cc",
-    "cdm_manager.h",
-    "cdm_manager_export.h",
-  ]
-  configs += [ ":cdm_manager_implementation" ]
-  deps = [
-    "//base",
-    "//media",
-  ]
-}
-
 static_library("cdm_paths") {
   sources = [
     "cdm_paths.cc",
diff --git a/media/cdm/cdm_manager.cc b/media/cdm/cdm_manager.cc
deleted file mode 100644
index 277333ca..0000000
--- a/media/cdm/cdm_manager.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/cdm/cdm_manager.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "media/base/content_decryption_module.h"
-
-namespace media {
-
-CdmManager::CdmManager() = default;
-
-CdmManager::~CdmManager() = default;
-
-// static
-CdmManager* CdmManager::GetInstance() {
-  static CdmManager* manager = new CdmManager();
-  return manager;
-}
-
-scoped_refptr<ContentDecryptionModule> CdmManager::GetCdm(int cdm_id) {
-  base::AutoLock lock(lock_);
-  auto iter = cdm_map_.find(cdm_id);
-  return iter == cdm_map_.end() ? nullptr : iter->second;
-}
-
-void CdmManager::RegisterCdm(int cdm_id,
-                             scoped_refptr<ContentDecryptionModule> cdm) {
-  base::AutoLock lock(lock_);
-  DCHECK(!cdm_map_.count(cdm_id));
-  cdm_map_[cdm_id] = cdm;
-}
-
-void CdmManager::UnregisterCdm(int cdm_id) {
-  base::AutoLock lock(lock_);
-  DCHECK(cdm_map_.count(cdm_id));
-  cdm_map_.erase(cdm_id);
-}
-
-}  // namespace media
diff --git a/media/cdm/cdm_manager.h b/media/cdm/cdm_manager.h
deleted file mode 100644
index 20d32b0..0000000
--- a/media/cdm/cdm_manager.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_CDM_CDM_MANAGER_H_
-#define MEDIA_CDM_CDM_MANAGER_H_
-
-#include <map>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
-#include "base/thread_annotations.h"
-#include "media/base/media_export.h"
-#include "media/cdm/cdm_manager_export.h"
-
-namespace media {
-
-class ContentDecryptionModule;
-
-// Provides a singleton registry of CDM instances. This is used to share
-// ContentDecryptionModules between the MojoMediaService and
-// AndroidVideoDecodeAccelerator, and should be removed along with AVDA in the
-// future. (MojoCdmServiceContext serves the same purpose for Media Mojo
-// services, but scoped to a single InterfaceFactory.)
-class CDM_MANAGER_EXPORT CdmManager {
- public:
-  CdmManager();
-  ~CdmManager();
-
-  static CdmManager* GetInstance();
-
-  // Returns the CDM associated with |cdm_id|. Can be called on any thread.
-  scoped_refptr<ContentDecryptionModule> GetCdm(int cdm_id);
-
-  // Registers the |cdm| for |cdm_id|.
-  void RegisterCdm(int cdm_id, scoped_refptr<ContentDecryptionModule> cdm);
-
-  // Unregisters the CDM associated with |cdm_id|.
-  void UnregisterCdm(int cdm_id);
-
- private:
-  base::Lock lock_;
-  std::map<int, scoped_refptr<ContentDecryptionModule>> cdm_map_
-      GUARDED_BY(lock_);
-
-  DISALLOW_COPY_AND_ASSIGN(CdmManager);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_CDM_CDM_MANAGER_H_
diff --git a/media/cdm/cdm_manager_export.h b/media/cdm/cdm_manager_export.h
deleted file mode 100644
index fc65a72..0000000
--- a/media/cdm/cdm_manager_export.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_CDM_CDM_MANAGER_EXPORT_H_
-#define MEDIA_CDM_CDM_MANAGER_EXPORT_H_
-
-// Define CDM_MANAGER_EXPORT so that functionality implemented by the
-// cdm_manager component can be exported to consumers.
-
-#if defined(COMPONENT_BUILD)
-#if defined(WIN32)
-
-#if defined(CDM_MANAGER_IMPLEMENTATION)
-#define CDM_MANAGER_EXPORT __declspec(dllexport)
-#else
-#define CDM_MANAGER_EXPORT __declspec(dllimport)
-#endif  // defined(CDM_MANAGER_IMPLEMENTATION)
-
-#else  // defined(WIN32)
-#if defined(CDM_MANAGER_IMPLEMENTATION)
-#define CDM_MANAGER_EXPORT __attribute__((visibility("default")))
-#else
-#define CDM_MANAGER_EXPORT
-#endif
-#endif
-
-#else  // defined(COMPONENT_BUILD)
-#define CDM_MANAGER_EXPORT
-#endif
-
-#endif  // MEDIA_BASE_CDM_MANAGER_EXPORT_H_
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index f057b8e..19b1a1e1 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -455,8 +455,6 @@
   }
 
   memcpy(shared_memory->memory(), buffer->data(), size);
-  // AndroidVideoDecodeAccelerator needs the timestamp to output frames in
-  // presentation order.
   BitstreamBuffer bitstream_buffer(
       next_bitstream_buffer_id_, shared_memory->handle(), false /* read_only */,
       size, 0, buffer->timestamp());
diff --git a/media/formats/mp4/dolby_vision.cc b/media/formats/mp4/dolby_vision.cc
index eac7831..9462dd4 100644
--- a/media/formats/mp4/dolby_vision.cc
+++ b/media/formats/mp4/dolby_vision.cc
@@ -65,6 +65,12 @@
     case 7:
       codec_profile = DOLBYVISION_PROFILE7;
       break;
+    case 8:
+      codec_profile = DOLBYVISION_PROFILE8;
+      break;
+    case 9:
+      codec_profile = DOLBYVISION_PROFILE9;
+      break;
     default:
       DVLOG(2) << "Deprecated or invalid Dolby Vision profile:"
                << static_cast<int>(dv_profile);
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index b03efdd..e679efd 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -91,21 +91,12 @@
 
   if (is_android) {
     sources += [
-      "android/android_video_decode_accelerator.cc",
-      "android/android_video_decode_accelerator.h",
       "android/android_video_encode_accelerator.cc",
       "android/android_video_encode_accelerator.h",
       "android/android_video_surface_chooser.cc",
       "android/android_video_surface_chooser.h",
       "android/android_video_surface_chooser_impl.cc",
       "android/android_video_surface_chooser_impl.h",
-      "android/avda_codec_image.cc",
-      "android/avda_codec_image.h",
-      "android/avda_picture_buffer_manager.cc",
-      "android/avda_picture_buffer_manager.h",
-      "android/avda_shared_state.cc",
-      "android/avda_shared_state.h",
-      "android/avda_state_provider.h",
       "android/avda_surface_bundle.cc",
       "android/avda_surface_bundle.h",
       "android/codec_allocator.cc",
@@ -150,13 +141,6 @@
     if (enable_vulkan) {
       deps += [ "//gpu/vulkan:vulkan" ]
     }
-
-    # TODO(crbug.com/789435): This is needed for AVDA to access the CDM
-    # directly.  Remove this dependency after VDAs are also running as part of
-    # the mojo media service. See http://crbug.com/522298
-    if (mojo_media_host == "gpu") {
-      deps += [ "//media/cdm:cdm_manager" ]
-    }
   }
 
   if (use_v4l2_codec) {
@@ -455,7 +439,6 @@
   if (is_android) {
     testonly = true
     sources = [
-      "android/android_video_decode_accelerator_unittest.cc",
       "android/android_video_surface_chooser_impl_unittest.cc",
       "android/codec_allocator_unittest.cc",
       "android/codec_image_group_unittest.cc",
diff --git a/media/gpu/android/android_video_decode_accelerator.cc b/media/gpu/android/android_video_decode_accelerator.cc
deleted file mode 100644
index 934c4a7..0000000
--- a/media/gpu/android/android_video_decode_accelerator.cc
+++ /dev/null
@@ -1,1836 +0,0 @@
-// Copyright (c) 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 "media/gpu/android/android_video_decode_accelerator.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/android/build_info.h"
-#include "base/auto_reset.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback_helpers.h"
-#include "base/command_line.h"
-#include "base/containers/queue.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/system/sys_info.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_checker.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/trace_event.h"
-#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
-#include "gpu/command_buffer/service/mailbox_manager.h"
-#include "gpu/ipc/service/gpu_channel.h"
-#include "media/base/android/media_codec_bridge_impl.h"
-#include "media/base/android/media_codec_util.h"
-#include "media/base/bind_to_current_loop.h"
-#include "media/base/bitstream_buffer.h"
-#include "media/base/limits.h"
-#include "media/base/media.h"
-#include "media/base/media_switches.h"
-#include "media/base/media_util.h"
-#include "media/base/timestamp_constants.h"
-#include "media/base/unaligned_shared_memory.h"
-#include "media/base/video_decoder_config.h"
-#include "media/gpu/android/android_video_surface_chooser_impl.h"
-#include "media/gpu/android/avda_picture_buffer_manager.h"
-#include "media/gpu/android/device_info.h"
-#include "media/gpu/android/promotion_hint_aggregator_impl.h"
-#include "media/media_buildflags.h"
-#include "media/mojo/buildflags.h"
-#include "media/video/picture.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
-#include "ui/gl/android/scoped_java_surface.h"
-#include "ui/gl/android/surface_texture.h"
-#include "ui/gl/gl_bindings.h"
-
-#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-#include "media/cdm/cdm_manager.h"  // nogncheck
-#endif
-
-#define NOTIFY_ERROR(error_code, error_message)      \
-  do {                                               \
-    DLOG(ERROR) << error_message;                    \
-    NotifyError(VideoDecodeAccelerator::error_code); \
-  } while (0)
-
-namespace media {
-
-namespace {
-
-enum { kNumPictureBuffers = limits::kMaxVideoFrames + 1 };
-
-// Max number of bitstreams notified to the client with
-// NotifyEndOfBitstreamBuffer() before getting output from the bitstream.
-enum { kMaxBitstreamsNotifiedInAdvance = 32 };
-
-// MediaCodec is only guaranteed to support baseline, but some devices may
-// support others. Advertise support for all H264 profiles and let the
-// MediaCodec fail when decoding if it's not actually supported. It's assumed
-// that consumers won't have software fallback for H264 on Android anyway.
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-constexpr VideoCodecProfile kSupportedH264Profiles[] = {
-    H264PROFILE_BASELINE,
-    H264PROFILE_MAIN,
-    H264PROFILE_EXTENDED,
-    H264PROFILE_HIGH,
-    H264PROFILE_HIGH10PROFILE,
-    H264PROFILE_HIGH422PROFILE,
-    H264PROFILE_HIGH444PREDICTIVEPROFILE,
-    H264PROFILE_SCALABLEBASELINE,
-    H264PROFILE_SCALABLEHIGH,
-    H264PROFILE_STEREOHIGH,
-    H264PROFILE_MULTIVIEWHIGH};
-
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
-constexpr VideoCodecProfile kSupportedHevcProfiles[] = {HEVCPROFILE_MAIN,
-                                                        HEVCPROFILE_MAIN10};
-#endif
-#endif
-
-// Because MediaCodec is thread-hostile (must be poked on a single thread) and
-// has no callback mechanism (b/11990118), we must drive it by polling for
-// complete frames (and available input buffers, when the codec is fully
-// saturated).  This function defines the polling delay.  The value used is an
-// arbitrary choice that trades off CPU utilization (spinning) against latency.
-// Mirrors android_video_encode_accelerator.cc:EncodePollDelay().
-//
-// An alternative to this polling scheme could be to dedicate a new thread
-// (instead of using the ChildThread) to run the MediaCodec, and make that
-// thread use the timeout-based flavor of MediaCodec's dequeue methods when it
-// believes the codec should complete "soon" (e.g. waiting for an input
-// buffer, or waiting for a picture when it knows enough complete input
-// pictures have been fed to saturate any internal buffering).  This is
-// speculative and it's unclear that this would be a win (nor that there's a
-// reasonably device-agnostic way to fill in the "believes" above).
-constexpr base::TimeDelta DecodePollDelay =
-    base::TimeDelta::FromMilliseconds(10);
-
-constexpr base::TimeDelta NoWaitTimeOut = base::TimeDelta::FromMicroseconds(0);
-
-constexpr base::TimeDelta IdleTimerTimeOut = base::TimeDelta::FromSeconds(1);
-
-// On low end devices (< KitKat is always low-end due to buggy MediaCodec),
-// defer the surface creation until the codec is actually used if we know no
-// software fallback exists.
-bool ShouldDeferSurfaceCreation(CodecAllocator* codec_allocator,
-                                const OverlayInfo& overlay_info,
-                                VideoCodec codec,
-                                DeviceInfo* device_info) {
-  // TODO(liberato): We might still want to defer if we've got a routing
-  // token.  It depends on whether we want to use it right away or not.
-  if (overlay_info.HasValidRoutingToken())
-    return false;
-
-  return codec == kCodecH264 && codec_allocator->IsAnyRegisteredAVDA() &&
-         device_info->SdkVersion() <= base::android::SDK_VERSION_JELLY_BEAN_MR2;
-}
-
-bool HasValidCdm(int cdm_id) {
-#if !BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-  return false;
-#else
-  auto cdm = CdmManager::GetInstance()->GetCdm(cdm_id);
-  if (!cdm) {
-    // This could happen during the destruction of the media element and the CDM
-    // and due to IPC CDM could be destroyed before the decoder.
-    DVLOG(1) << "CDM not available.";
-    return false;
-  }
-
-  auto* cdm_context = cdm->GetCdmContext();
-  auto* media_crypto_context =
-      cdm_context ? cdm_context->GetMediaCryptoContext() : nullptr;
-  // This could happen if the CDM is not MediaDrmBridge, which could happen in
-  // test cases.
-  if (!media_crypto_context) {
-    DVLOG(1) << "MediaCryptoContext not available.";
-    return false;
-  }
-
-  return true;
-#endif
-}
-
-}  // namespace
-
-// AVDAManager manages a RepeatingTimer so that AVDAs can get a regular callback
-// to DoIOTask().
-class AVDAManager {
- public:
-  AVDAManager() {}
-
-  // Request periodic callback of |avda|->DoIOTask(). Does nothing if the
-  // instance is already registered and the timer started. The first request
-  // will start the repeating timer on an interval of DecodePollDelay.
-  void StartTimer(AndroidVideoDecodeAccelerator* avda) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-
-    timer_avda_instances_.insert(avda);
-
-    // If the timer is running, StopTimer() might have been called earlier, if
-    // so remove the instance from the pending erasures.
-    if (timer_running_)
-      pending_erase_.erase(avda);
-
-    if (io_timer_.IsRunning())
-      return;
-    io_timer_.Start(FROM_HERE, DecodePollDelay, this, &AVDAManager::RunTimer);
-  }
-
-  // Stop callbacks to |avda|->DoIOTask(). Does nothing if the instance is not
-  // registered. If there are no instances left, the repeating timer will be
-  // stopped.
-  void StopTimer(AndroidVideoDecodeAccelerator* avda) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-
-    // If the timer is running, defer erasures to avoid iterator invalidation.
-    if (timer_running_) {
-      pending_erase_.insert(avda);
-      return;
-    }
-
-    timer_avda_instances_.erase(avda);
-    if (timer_avda_instances_.empty())
-      io_timer_.Stop();
-  }
-
- private:
-  ~AVDAManager() = delete;
-
-  void RunTimer() {
-    {
-      // Call out to all AVDA instances, some of which may attempt to remove
-      // themselves from the list during this operation; those removals will be
-      // deferred until after all iterations are complete.
-      base::AutoReset<bool> scoper(&timer_running_, true);
-      for (auto* avda : timer_avda_instances_)
-        avda->DoIOTask(false);
-    }
-
-    // Take care of any deferred erasures.
-    for (auto* avda : pending_erase_)
-      StopTimer(avda);
-    pending_erase_.clear();
-
-    // TODO(dalecurtis): We may want to consider chunking this if task execution
-    // takes too long for the combined timer.
-  }
-
-  // All AVDA instances that would like us to poll DoIOTask.
-  std::set<AndroidVideoDecodeAccelerator*> timer_avda_instances_;
-
-  // Since we can't delete while iterating when using a set, defer erasure until
-  // after iteration complete.
-  bool timer_running_ = false;
-  std::set<AndroidVideoDecodeAccelerator*> pending_erase_;
-
-  // Repeating timer responsible for draining pending IO to the codecs.
-  base::RepeatingTimer io_timer_;
-
-  base::ThreadChecker thread_checker_;
-
-  DISALLOW_COPY_AND_ASSIGN(AVDAManager);
-};
-
-static AVDAManager* GetManager() {
-  static AVDAManager* manager = new AVDAManager();
-  return manager;
-}
-
-AndroidVideoDecodeAccelerator::BitstreamRecord::BitstreamRecord(
-    BitstreamBuffer bitstream_buffer)
-    : buffer(std::move(bitstream_buffer)) {
-  if (buffer.id() != -1) {
-    memory.reset(
-        new UnalignedSharedMemory(buffer.TakeRegion(), buffer.size(), true));
-  }
-}
-
-AndroidVideoDecodeAccelerator::BitstreamRecord::BitstreamRecord(
-    BitstreamRecord&& other) = default;
-
-AndroidVideoDecodeAccelerator::BitstreamRecord::~BitstreamRecord() {}
-
-AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator(
-    CodecAllocator* codec_allocator,
-    std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser,
-    const MakeGLContextCurrentCallback& make_context_current_cb,
-    const GetContextGroupCallback& get_context_group_cb,
-    const AndroidOverlayMojoFactoryCB& overlay_factory_cb,
-    const CreateAbstractTextureCallback& create_abstract_texture_cb,
-    DeviceInfo* device_info)
-    : client_(nullptr),
-      codec_allocator_(codec_allocator),
-      make_context_current_cb_(make_context_current_cb),
-      get_context_group_cb_(get_context_group_cb),
-      state_(BEFORE_OVERLAY_INIT),
-      picturebuffers_requested_(false),
-      picture_buffer_manager_(this),
-      media_crypto_context_(nullptr),
-      cdm_registration_id_(0),
-      pending_input_buf_index_(-1),
-      during_initialize_(false),
-      deferred_initialization_pending_(false),
-      codec_needs_reset_(false),
-      defer_surface_creation_(false),
-      surface_chooser_helper_(
-          std::move(surface_chooser),
-          base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kForceVideoOverlays),
-          base::FeatureList::IsEnabled(media::kUseAndroidOverlayAggressively),
-          false /* always_use_texture_owner */),
-      device_info_(device_info),
-      force_defer_surface_creation_for_testing_(false),
-      force_allow_software_decoding_for_testing_(false),
-      overlay_factory_cb_(overlay_factory_cb),
-      create_abstract_texture_cb_(create_abstract_texture_cb),
-      weak_this_factory_(this) {}
-
-AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  GetManager()->StopTimer(this);
-  codec_allocator_->StopThread(this);
-
-#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-  if (!media_crypto_context_)
-    return;
-
-  // Cancel previously registered callback (if any).
-  media_crypto_context_->SetMediaCryptoReadyCB(
-      MediaCryptoContext::MediaCryptoReadyCB());
-
-  if (cdm_registration_id_)
-    media_crypto_context_->UnregisterPlayer(cdm_registration_id_);
-#endif  // BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-}
-
-bool AndroidVideoDecodeAccelerator::Initialize(const Config& config,
-                                               Client* client) {
-  DVLOG(1) << __func__ << ": " << config.AsHumanReadableString();
-  TRACE_EVENT0("media", "AVDA::Initialize");
-  DCHECK(!media_codec_);
-  DCHECK(thread_checker_.CalledOnValidThread());
-  base::AutoReset<bool> scoper(&during_initialize_, true);
-
-  if (!make_context_current_cb_ || !get_context_group_cb_) {
-    DLOG(ERROR) << "GL callbacks are required for this VDA";
-    return false;
-  }
-
-  if (config.output_mode != Config::OutputMode::ALLOCATE) {
-    DLOG(ERROR) << "Only ALLOCATE OutputMode is supported by this VDA";
-    return false;
-  }
-
-  DCHECK(client);
-  client_ = client;
-  config_ = config;
-  codec_config_ = new CodecConfig();
-  codec_config_->codec = VideoCodecProfileToVideoCodec(config.profile);
-  codec_config_->initial_expected_coded_size =
-      config.initial_expected_coded_size;
-
-  switch (codec_config_->codec) {
-    case kCodecVP8:
-    case kCodecVP9:
-      break;
-
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-    case kCodecH264:
-      codec_config_->csd0 = config.sps;
-      codec_config_->csd1 = config.pps;
-      break;
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
-    case kCodecHEVC:
-      break;
-#endif
-#endif
-    default:
-      DLOG(ERROR) << "Unsupported profile: " << GetProfileName(config.profile);
-      return false;
-  }
-
-  codec_config_->software_codec_forbidden =
-      IsMediaCodecSoftwareDecodingForbidden();
-
-  codec_config_->container_color_space = config.container_color_space;
-  codec_config_->hdr_metadata = config.hdr_metadata;
-
-  // Only use MediaCodec for VP8/9 if it's likely backed by hardware
-  // or if the stream is encrypted.
-  if (IsMediaCodecSoftwareDecodingForbidden() &&
-      MediaCodecUtil::IsKnownUnaccelerated(codec_config_->codec,
-                                           MediaCodecDirection::DECODER)) {
-    DVLOG(1) << "Initialization failed: " << GetCodecName(codec_config_->codec)
-             << " is not hardware accelerated";
-    return false;
-  }
-
-  auto* context_group = get_context_group_cb_.Run();
-  if (!context_group) {
-    DLOG(ERROR) << "Failed to get context group.";
-    return false;
-  }
-
-  // We signaled that we support deferred initialization, so see if the client
-  // does also.
-  deferred_initialization_pending_ = config.is_deferred_initialization_allowed;
-
-  // If we're low on resources, we may decide to defer creation of the surface
-  // until the codec is actually used.
-  if (force_defer_surface_creation_for_testing_ ||
-      ShouldDeferSurfaceCreation(codec_allocator_, config_.overlay_info,
-                                 codec_config_->codec, device_info_)) {
-    // We should never be here if a SurfaceView is required.
-    // TODO(liberato): This really isn't true with AndroidOverlay.
-    defer_surface_creation_ = true;
-  }
-
-  codec_allocator_->StartThread(this);
-
-  // If has valid CDM, start by initializing the CDM, even for clear stream.
-  if (HasValidCdm(config_.cdm_id) && deferred_initialization_pending_) {
-    InitializeCdm();
-    return state_ != ERROR;
-  }
-
-  // Cannot handle encrypted stream without valid CDM.
-  if (config_.is_encrypted()) {
-    DLOG(ERROR) << "Deferred initialization must be used for encrypted streams";
-    return false;
-  }
-
-  StartSurfaceChooser();
-
-  // Fail / complete / defer initialization.
-  return state_ != ERROR;
-}
-
-void AndroidVideoDecodeAccelerator::StartSurfaceChooser() {
-  DCHECK_EQ(state_, BEFORE_OVERLAY_INIT);
-
-  // If we're trying to defer surface creation, then don't notify the chooser
-  // that it may start getting surfaces yet.  We'll do that later.
-  if (defer_surface_creation_) {
-    if (deferred_initialization_pending_)
-      NotifyInitializationSucceeded();
-    return;
-  }
-
-  surface_chooser_helper_.SetIsFullscreen(config_.overlay_info.is_fullscreen);
-
-  surface_chooser_helper_.chooser()->SetClientCallbacks(
-      base::Bind(&AndroidVideoDecodeAccelerator::OnSurfaceTransition,
-                 weak_this_factory_.GetWeakPtr()),
-      base::Bind(&AndroidVideoDecodeAccelerator::OnSurfaceTransition,
-                 weak_this_factory_.GetWeakPtr(), nullptr));
-
-  // Handle the sync path, which must use TextureOwner anyway.  Note that we
-  // check both |during_initialize_| and |deferred_initialization_pending_|,
-  // since we might get here during deferred surface creation.  In that case,
-  // Decode will call us (after clearing |defer_surface_creation_|), but
-  // deferred init will have already been signaled optimistically as success.
-  //
-  // Also note that we might choose to defer surface creation for the sync path,
-  // which won't get here.  We'll exit above, successfully, during init, and
-  // will fall through to the below when Decode calls us back.  That's okay.
-  // We only handle this case specially since |surface_chooser_| is allowed to
-  // post callbacks to us.  Here, we guarantee that the sync case is actually
-  // resolved synchronously.  The only exception will be if we need to defer
-  // surface creation for other reasons, in which case the sync path with just
-  // signal success optimistically.
-  if (during_initialize_ && !deferred_initialization_pending_) {
-    DCHECK(!config_.overlay_info.HasValidRoutingToken());
-    // Note that we might still send feedback to |surface_chooser_|, which might
-    // call us back.  However, it will only ever tell us to use TextureOwner,
-    // since we have no overlay factory anyway.
-    OnSurfaceTransition(nullptr);
-    return;
-  }
-
-  // If we have a surface, then notify |surface_chooser_| about it.  If we were
-  // told not to use an overlay (kNoSurfaceID or a null routing token), then we
-  // leave the factory blank.
-  AndroidOverlayFactoryCB factory;
-  if (config_.overlay_info.HasValidRoutingToken() && overlay_factory_cb_) {
-    factory = base::BindRepeating(overlay_factory_cb_,
-                                  *config_.overlay_info.routing_token);
-  }
-
-  // Notify |surface_chooser_| that we've started.  This guarantees that we'll
-  // get a callback.  It might not be a synchronous callback, but we're not in
-  // the synchronous case.  It will be soon, though.  For pre-M, we rely on the
-  // fact that |surface_chooser_| won't tell us to use a TextureOwner while
-  // waiting for an overlay to become ready, for example.
-  surface_chooser_helper_.UpdateChooserState(std::move(factory));
-}
-
-void AndroidVideoDecodeAccelerator::OnSurfaceTransition(
-    std::unique_ptr<AndroidOverlay> overlay) {
-  if (overlay) {
-    overlay->AddSurfaceDestroyedCallback(base::Bind(
-        &AndroidVideoDecodeAccelerator::OnStopUsingOverlayImmediately,
-        weak_this_factory_.GetWeakPtr()));
-  }
-
-  // If we're waiting for a surface (e.g., during startup), then proceed
-  // immediately.  Otherwise, wait for Dequeue to handle it.  This can probably
-  // be merged with UpdateSurface.
-  if (state_ == BEFORE_OVERLAY_INIT) {
-    DCHECK(!incoming_overlay_);
-    incoming_bundle_ = new AVDASurfaceBundle(std::move(overlay));
-    InitializePictureBufferManager();
-    return;
-  }
-
-  // If, for some reason, |surface_chooser_| decides that we really should
-  // change our output surface pre-M, ignore it.  For example, if the
-  // compositor tells us that it can't use an overlay, well, there's not much
-  // that we can do here unless we start falling forward to keyframes.
-  if (!device_info_->IsSetOutputSurfaceSupported())
-    return;
-
-  // If we're using a TextureOwner and are told to switch to one, then just
-  // do nothing.  |surface_chooser_| doesn't really know if we've switched to
-  // TextureOwner or not.  Note that it can't ask us to switch to the same
-  // overlay we're using, since it's unique_ptr.
-  if (!overlay && codec_config_->surface_bundle &&
-      !codec_config_->surface_bundle->overlay) {
-    // Also stop transitioning to an overlay, if we were doing so.
-    incoming_overlay_.reset();
-    return;
-  }
-
-  incoming_overlay_ = std::move(overlay);
-}
-
-void AndroidVideoDecodeAccelerator::InitializePictureBufferManager() {
-  DCHECK(!defer_surface_creation_);
-  DCHECK(incoming_bundle_);
-
-  if (!make_context_current_cb_.Run()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE,
-                 "Failed to make this decoder's GL context current");
-    incoming_bundle_ = nullptr;
-    return;
-  }
-
-  // Move |incoming_bundle_| to |codec_config_|.  Our caller must set up an
-  // incoming bundle properly, since we don't want to accidentally overwrite
-  // |surface_bundle| for a codec that's being released elsewhere.
-  // TODO(liberato): it doesn't make sense anymore for the PictureBufferManager
-  // to create the texture owner.  We can probably make an overlay impl out
-  // of it, and provide the texture owner to |picture_buffer_manager_|.
-  if (!picture_buffer_manager_.Initialize(incoming_bundle_)) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Could not allocate texture owner");
-    incoming_bundle_ = nullptr;
-    return;
-  }
-
-  // If we have a media codec, then SetSurface.  If that doesn't work, then we
-  // do not try to allocate a new codec; we might not be at a keyframe, etc.
-  // If we get here with a codec, then we must setSurface.
-  if (media_codec_) {
-    // TODO(liberato): fail on api check?
-    if (!media_codec_->SetSurface(incoming_bundle_->GetJavaSurface())) {
-      NOTIFY_ERROR(PLATFORM_FAILURE, "MediaCodec failed to switch surfaces.");
-      // We're not going to use |incoming_bundle_|.
-    } else {
-      // We've switched surfaces, so replace |surface_bundle|.
-      codec_config_->surface_bundle = incoming_bundle_;
-      // We could be in BEFORE_OVERLAY_INIT, but we're not anymore.
-      state_ = NO_ERROR;
-    }
-    incoming_bundle_ = nullptr;
-    CacheFrameInformation();
-    return;
-  }
-
-  // We're going to create a codec with |incoming_bundle_|.  It might fail, but
-  // either way, we're done with any previous bundle.  Note that, since we
-  // never get here after init (i.e., we never change surfaces without using
-  // SetSurface), there shouldn't be any previous bundle.  However, this is the
-  // right thing to do even if we can switch.
-  codec_config_->surface_bundle = incoming_bundle_;
-  incoming_bundle_ = nullptr;
-  CacheFrameInformation();
-
-  // If the client doesn't support deferred initialization (WebRTC), then we
-  // should complete it now and return a meaningful result.  Note that it would
-  // be nice if we didn't have to worry about starting codec configuration at
-  // all (::Initialize or the wrapper can do it), but then they have to remember
-  // not to start codec config if we have to wait for the cdm.  It's somewhat
-  // clearer for us to handle both cases.
-  // For this to be a case for sync configuration, we must be called from
-  // Initialize(), and the client must not want deferred init.  Note that having
-  // |deferred_initialization_pending_| false by itself isn't enough; if we're
-  // deferring surface creation, then we'll finish deferred init before asking
-  // for the surface.  We'll be called via Decode.
-  if (during_initialize_ && !deferred_initialization_pending_) {
-    ConfigureMediaCodecSynchronously();
-    return;
-  }
-
-  // In all other cases, we don't have to wait for the codec.
-  ConfigureMediaCodecAsynchronously();
-}
-
-void AndroidVideoDecodeAccelerator::DoIOTask(bool start_timer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT0("media", "AVDA::DoIOTask");
-  if (state_ == ERROR || state_ == WAITING_FOR_CODEC ||
-      state_ == SURFACE_DESTROYED || state_ == BEFORE_OVERLAY_INIT) {
-    return;
-  }
-
-  picture_buffer_manager_.MaybeRenderEarly();
-  bool did_work = false, did_input = false, did_output = false;
-  do {
-    did_input = QueueInput();
-    did_output = DequeueOutput();
-    if (did_input || did_output)
-      did_work = true;
-  } while (did_input || did_output);
-
-  ManageTimer(did_work || start_timer);
-}
-
-bool AndroidVideoDecodeAccelerator::QueueInput() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT0("media", "AVDA::QueueInput");
-  if (state_ == ERROR || state_ == WAITING_FOR_CODEC ||
-      state_ == WAITING_FOR_KEY || state_ == BEFORE_OVERLAY_INIT) {
-    return false;
-  }
-  if (bitstreams_notified_in_advance_.size() > kMaxBitstreamsNotifiedInAdvance)
-    return false;
-  if (pending_bitstream_records_.empty())
-    return false;
-
-  int input_buf_index = pending_input_buf_index_;
-
-  // Do not dequeue a new input buffer if we failed with MEDIA_CODEC_NO_KEY.
-  // That status does not return this buffer back to the pool of
-  // available input buffers. We have to reuse it in QueueSecureInputBuffer().
-  if (input_buf_index == -1) {
-    MediaCodecStatus status =
-        media_codec_->DequeueInputBuffer(NoWaitTimeOut, &input_buf_index);
-    switch (status) {
-      case MEDIA_CODEC_TRY_AGAIN_LATER:
-        return false;
-      case MEDIA_CODEC_ERROR:
-        NOTIFY_ERROR(PLATFORM_FAILURE, "DequeueInputBuffer failed");
-        return false;
-      case MEDIA_CODEC_OK:
-        break;
-      default:
-        NOTREACHED();
-        return false;
-    }
-  }
-
-  DCHECK_NE(input_buf_index, -1);
-
-  BitstreamBuffer* bitstream_buffer =
-      &pending_bitstream_records_.front().buffer;
-
-  if (bitstream_buffer->id() == -1) {
-    pending_bitstream_records_.pop();
-    TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount",
-                   pending_bitstream_records_.size());
-
-    media_codec_->QueueEOS(input_buf_index);
-    return true;
-  }
-
-  std::unique_ptr<UnalignedSharedMemory> shm;
-
-  if (pending_input_buf_index_ == -1) {
-    // When |pending_input_buf_index_| is not -1, the buffer is already dequeued
-    // from MediaCodec and filled with data. The buffer shared memory handle is
-    // held by the front pending bitstream record.
-    shm = std::move(pending_bitstream_records_.front().memory);
-
-    if (!shm->MapAt(bitstream_buffer->offset(), bitstream_buffer->size())) {
-      NOTIFY_ERROR(UNREADABLE_INPUT, "UnalignedSharedMemory::Map() failed");
-      return false;
-    }
-  }
-
-  const base::TimeDelta presentation_timestamp =
-      bitstream_buffer->presentation_timestamp();
-  DCHECK(presentation_timestamp != kNoTimestamp)
-      << "Bitstream buffers must have valid presentation timestamps";
-
-  // There may already be a bitstream buffer with this timestamp, e.g., VP9 alt
-  // ref frames, but it's OK to overwrite it because we only expect a single
-  // output frame to have that timestamp. AVDA clients only use the bitstream
-  // buffer id in the returned Pictures to map a bitstream buffer back to a
-  // timestamp on their side, so either one of the bitstream buffer ids will
-  // result in them finding the right timestamp.
-  bitstream_buffers_in_decoder_[presentation_timestamp] =
-      bitstream_buffer->id();
-
-  // Notice that |memory| will be null if we repeatedly enqueue the same buffer,
-  // this happens after MEDIA_CODEC_NO_KEY.
-  const uint8_t* memory =
-      shm ? static_cast<const uint8_t*>(shm->memory()) : nullptr;
-  const std::string& key_id = bitstream_buffer->key_id();
-  const std::string& iv = bitstream_buffer->iv();
-  const std::vector<SubsampleEntry>& subsamples =
-      bitstream_buffer->subsamples();
-
-  MediaCodecStatus status;
-  if (key_id.empty() || iv.empty()) {
-    status = media_codec_->QueueInputBuffer(input_buf_index, memory,
-                                            bitstream_buffer->size(),
-                                            presentation_timestamp);
-  } else {
-    // VDAs only support "cenc" encryption scheme.
-    status = media_codec_->QueueSecureInputBuffer(
-        input_buf_index, memory, bitstream_buffer->size(), key_id, iv,
-        subsamples, AesCtrEncryptionScheme(), presentation_timestamp);
-  }
-
-  DVLOG(2) << __func__
-           << ": Queue(Secure)InputBuffer: pts:" << presentation_timestamp
-           << " status:" << status;
-
-  if (status == MEDIA_CODEC_NO_KEY) {
-    // Keep trying to enqueue the same input buffer.
-    // The buffer is owned by us (not the MediaCodec) and is filled with data.
-    DVLOG(1) << "QueueSecureInputBuffer failed: NO_KEY";
-    pending_input_buf_index_ = input_buf_index;
-    state_ = WAITING_FOR_KEY;
-    return false;
-  }
-
-  pending_input_buf_index_ = -1;
-  // Popping the pending record invalides |bitstream_buffer|.
-  int32_t pending_bitstream_buffer_id = bitstream_buffer->id();
-  pending_bitstream_records_.pop();
-  TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount",
-                 pending_bitstream_records_.size());
-  // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output
-  // will be returned from the bitstream buffer. However, MediaCodec API is
-  // not enough to guarantee it.
-  // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to
-  // keep getting more bitstreams from the client, and throttle them by using
-  // |bitstreams_notified_in_advance_|.
-  // TODO(dwkang): check if there is a way to remove this workaround.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer,
-                     weak_this_factory_.GetWeakPtr(),
-                     pending_bitstream_buffer_id));
-  bitstreams_notified_in_advance_.push_back(pending_bitstream_buffer_id);
-
-  if (status != MEDIA_CODEC_OK) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "QueueInputBuffer failed:" << status);
-    return false;
-  }
-
-  return true;
-}
-
-bool AndroidVideoDecodeAccelerator::DequeueOutput() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT0("media", "AVDA::DequeueOutput");
-  if (state_ == ERROR || state_ == WAITING_FOR_CODEC ||
-      state_ == BEFORE_OVERLAY_INIT) {
-    return false;
-  }
-  // If we're draining for reset or destroy, then we don't need picture buffers
-  // since we won't send any decoded frames anyway.  There might not be any,
-  // since the pipeline might not be sending them back and / or they don't
-  // exist anymore.  From the pipeline's point of view, for Destroy at least,
-  // the VDA is already gone.
-  if (picturebuffers_requested_ && output_picture_buffers_.empty() &&
-      !IsDrainingForResetOrDestroy()) {
-    return false;
-  }
-  if (!output_picture_buffers_.empty() && free_picture_ids_.empty() &&
-      !IsDrainingForResetOrDestroy()) {
-    // Don't have any picture buffer to send. Need to wait.
-    return false;
-  }
-
-  // If we're waiting to switch surfaces pause output release until we have all
-  // picture buffers returned. This is so we can ensure the right flags are set
-  // on the picture buffers returned to the client.
-  if (incoming_overlay_) {
-    if (picture_buffer_manager_.HasUnrenderedPictures())
-      return false;
-    if (!UpdateSurface())
-      return false;
-
-    // UpdateSurface should fail if we've transitioned to the error state.
-    DCHECK(state_ == NO_ERROR);
-  }
-
-  bool eos = false;
-  base::TimeDelta presentation_timestamp;
-  int32_t buf_index = 0;
-  do {
-    size_t offset = 0;
-    size_t size = 0;
-
-    TRACE_EVENT_BEGIN0("media", "AVDA::DequeueOutput");
-    MediaCodecStatus status = media_codec_->DequeueOutputBuffer(
-        NoWaitTimeOut, &buf_index, &offset, &size, &presentation_timestamp,
-        &eos, NULL);
-    TRACE_EVENT_END2("media", "AVDA::DequeueOutput", "status", status,
-                     "presentation_timestamp (ms)",
-                     presentation_timestamp.InMilliseconds());
-
-    switch (status) {
-      case MEDIA_CODEC_ERROR:
-        // Do not post an error if we are draining for reset and destroy.
-        // Instead, signal completion of the drain.
-        if (IsDrainingForResetOrDestroy()) {
-          DVLOG(1) << __func__ << ": error while draining";
-          state_ = ERROR;
-          OnDrainCompleted();
-        } else {
-          NOTIFY_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed.");
-        }
-        return false;
-
-      case MEDIA_CODEC_TRY_AGAIN_LATER:
-        return false;
-
-      case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: {
-        // An OUTPUT_FORMAT_CHANGED is not reported after flush() if the frame
-        // size does not change. Therefore we have to keep track on the format
-        // even if draining, unless we are draining for destroy.
-        if (drain_type_ == DRAIN_FOR_DESTROY)
-          return true;  // ignore
-
-        if (media_codec_->GetOutputSize(&size_) != MEDIA_CODEC_OK) {
-          NOTIFY_ERROR(PLATFORM_FAILURE, "GetOutputSize failed.");
-          return false;
-        }
-
-        DVLOG(3) << __func__
-                 << " OUTPUT_FORMAT_CHANGED, new size: " << size_.ToString();
-
-        // Don't request picture buffers if we already have some. This avoids
-        // having to dismiss the existing buffers which may actively reference
-        // decoded images. Breaking their connection to the decoded image will
-        // cause rendering of black frames. Instead, we let the existing
-        // PictureBuffers live on and we simply update their size the next time
-        // they're attached to an image of the new resolution. See the
-        // size update in |SendDecodedFrameToClient| and https://crbug/587994.
-        if (output_picture_buffers_.empty() && !picturebuffers_requested_) {
-          picturebuffers_requested_ = true;
-          base::ThreadTaskRunnerHandle::Get()->PostTask(
-              FROM_HERE,
-              base::BindOnce(
-                  &AndroidVideoDecodeAccelerator::RequestPictureBuffers,
-                  weak_this_factory_.GetWeakPtr()));
-          return false;
-        }
-
-        return true;
-      }
-
-      case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
-        break;
-
-      case MEDIA_CODEC_OK:
-        DCHECK_GE(buf_index, 0);
-        DVLOG(3) << __func__ << ": pts:" << presentation_timestamp
-                 << " buf_index:" << buf_index << " offset:" << offset
-                 << " size:" << size << " eos:" << eos;
-        break;
-
-      default:
-        NOTREACHED();
-        break;
-    }
-  } while (buf_index < 0);
-
-  if (eos) {
-    OnDrainCompleted();
-    return false;
-  }
-
-  if (IsDrainingForResetOrDestroy()) {
-    media_codec_->ReleaseOutputBuffer(buf_index, false);
-    return true;
-  }
-
-  if (!picturebuffers_requested_) {
-    // In 0.01% of playbacks MediaCodec returns a frame before FORMAT_CHANGED.
-    // Occurs on JB and M. (See the Media.AVDA.MissingFormatChanged histogram.)
-    media_codec_->ReleaseOutputBuffer(buf_index, false);
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Dequeued buffers before FORMAT_CHANGED.");
-    return false;
-  }
-
-  // Get the bitstream buffer id from the timestamp.
-  auto it = bitstream_buffers_in_decoder_.find(presentation_timestamp);
-
-  if (it != bitstream_buffers_in_decoder_.end()) {
-    const int32_t bitstream_buffer_id = it->second;
-    bitstream_buffers_in_decoder_.erase(bitstream_buffers_in_decoder_.begin(),
-                                        ++it);
-    SendDecodedFrameToClient(buf_index, bitstream_buffer_id);
-
-    // Removes ids former or equal than the id from decoder. Note that
-    // |bitstreams_notified_in_advance_| does not mean bitstream ids in decoder
-    // because of frame reordering issue. We just maintain this roughly and use
-    // it for throttling.
-    for (auto bitstream_it = bitstreams_notified_in_advance_.begin();
-         bitstream_it != bitstreams_notified_in_advance_.end();
-         ++bitstream_it) {
-      if (*bitstream_it == bitstream_buffer_id) {
-        bitstreams_notified_in_advance_.erase(
-            bitstreams_notified_in_advance_.begin(), ++bitstream_it);
-        break;
-      }
-    }
-  } else {
-    // Normally we assume that the decoder makes at most one output frame for
-    // each distinct input timestamp. However MediaCodecBridge uses timestamp
-    // correction and provides a non-decreasing timestamp sequence, which might
-    // result in timestamp duplicates. Discard the frame if we cannot get the
-    // corresponding buffer id.
-    DVLOG(3) << __func__ << ": Releasing buffer with unexpected PTS: "
-             << presentation_timestamp;
-    media_codec_->ReleaseOutputBuffer(buf_index, false);
-  }
-
-  // We got a decoded frame, so try for another.
-  return true;
-}
-
-void AndroidVideoDecodeAccelerator::SendDecodedFrameToClient(
-    int32_t codec_buffer_index,
-    int32_t bitstream_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_NE(bitstream_id, -1);
-  DCHECK(!free_picture_ids_.empty());
-  TRACE_EVENT0("media", "AVDA::SendDecodedFrameToClient");
-
-  if (!make_context_current_cb_.Run()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to make the GL context current.");
-    return;
-  }
-
-  int32_t picture_buffer_id = free_picture_ids_.front();
-  free_picture_ids_.pop();
-  TRACE_COUNTER1("media", "AVDA::FreePictureIds", free_picture_ids_.size());
-
-  const auto it = output_picture_buffers_.find(picture_buffer_id);
-  if (it == output_picture_buffers_.end()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE,
-                 "Can't find PictureBuffer id: " << picture_buffer_id);
-    return;
-  }
-
-  PictureBuffer& picture_buffer = it->second;
-  const bool size_changed = picture_buffer.size() != size_;
-  if (size_changed)
-    picture_buffer.set_size(size_);
-
-  // Only ask for promotion hints if we can actually switch surfaces.
-  const bool want_promotion_hint = device_info_->IsSetOutputSurfaceSupported();
-  const bool allow_overlay = picture_buffer_manager_.ArePicturesOverlayable();
-
-  // TODO(liberato): remove in M63, if FrameInformation is clearly working.
-  UMA_HISTOGRAM_BOOLEAN("Media.AVDA.FrameSentAsOverlay", allow_overlay);
-
-  // Record the frame type that we're sending and some information about why.
-  UMA_HISTOGRAM_ENUMERATION(
-      "Media.AVDA.FrameInformation", cached_frame_information_,
-      static_cast<int>(
-          SurfaceChooserHelper::FrameInformation::FRAME_INFORMATION_MAX) +
-          1);  // PRESUBMIT_IGNORE_UMA_MAX
-
-  // We unconditionally mark the picture as overlayable, even if
-  // |!allow_overlay|, if we want to get hints.  It's required, else we won't
-  // get hints.
-  // TODO(hubbe): Insert the correct color space. http://crbug.com/647725
-  Picture picture(picture_buffer_id, bitstream_id, gfx::Rect(size_),
-                  gfx::ColorSpace(),
-                  want_promotion_hint ? true : allow_overlay);
-  picture.set_size_changed(size_changed);
-  if (want_promotion_hint) {
-    picture.set_wants_promotion_hint(true);
-    // This will prevent it from actually being promoted if it shouldn't be.
-    picture.set_texture_owner(!allow_overlay);
-  }
-
-  // Notify picture ready before calling UseCodecBufferForPictureBuffer() since
-  // that process may be slow and shouldn't delay delivery of the frame to the
-  // renderer. The picture is only used on the same thread as this method is
-  // called, so it is safe to do this.
-  NotifyPictureReady(picture);
-
-  // Connect the PictureBuffer to the decoded frame.
-  picture_buffer_manager_.UseCodecBufferForPictureBuffer(codec_buffer_index,
-                                                         picture_buffer);
-}
-
-void AndroidVideoDecodeAccelerator::Decode(BitstreamBuffer bitstream_buffer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // If we deferred getting a surface, then start getting one now.
-  if (defer_surface_creation_) {
-    // We should still be in BEFORE_OVERLAY_INIT, since we've deferred doing it
-    // until now.
-    DCHECK_EQ(state_, BEFORE_OVERLAY_INIT);
-    defer_surface_creation_ = false;
-    StartSurfaceChooser();
-    if (state_ == ERROR) {
-      DLOG(ERROR) << "Failed deferred surface and MediaCodec initialization.";
-      return;
-    }
-  }
-
-  // If we previously deferred a codec restart, take care of it now. This can
-  // happen on older devices where configuration changes require a codec reset.
-  if (codec_needs_reset_) {
-    DCHECK(!drain_type_);
-    ResetCodecState();
-  }
-
-  if (bitstream_buffer.id() >= 0 && bitstream_buffer.size() > 0) {
-    DecodeBuffer(std::move(bitstream_buffer));
-    return;
-  }
-
-  if (bitstream_buffer.id() < 0) {
-    NOTIFY_ERROR(INVALID_ARGUMENT,
-                 "Invalid bistream_buffer, id: " << bitstream_buffer.id());
-  } else {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            &AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer,
-            weak_this_factory_.GetWeakPtr(), bitstream_buffer.id()));
-  }
-}
-
-void AndroidVideoDecodeAccelerator::DecodeBuffer(
-    BitstreamBuffer bitstream_buffer) {
-  pending_bitstream_records_.push(BitstreamRecord(std::move(bitstream_buffer)));
-  TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount",
-                 pending_bitstream_records_.size());
-
-  DoIOTask(true);
-}
-
-void AndroidVideoDecodeAccelerator::RequestPictureBuffers() {
-  if (client_) {
-    // Allocate a picture buffer that is the actual frame size.  Note that it
-    // will be an external texture anyway, so it doesn't allocate an image of
-    // that size.  It's important to get the coded size right, so that
-    // VideoLayerImpl doesn't try to scale the texture when building the quad
-    // for it.
-    client_->ProvidePictureBuffers(kNumPictureBuffers, PIXEL_FORMAT_UNKNOWN, 1,
-                                   size_,
-                                   AVDAPictureBufferManager::kTextureTarget);
-  }
-}
-
-void AndroidVideoDecodeAccelerator::AssignPictureBuffers(
-    const std::vector<PictureBuffer>& buffers) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(output_picture_buffers_.empty());
-  DCHECK(free_picture_ids_.empty());
-
-  if (buffers.size() < kNumPictureBuffers) {
-    NOTIFY_ERROR(INVALID_ARGUMENT, "Not enough picture buffers assigned.");
-    return;
-  }
-
-  const bool have_context = make_context_current_cb_.Run();
-  LOG_IF(WARNING, !have_context)
-      << "Failed to make GL context current for Assign, continuing.";
-
-  for (size_t i = 0; i < buffers.size(); ++i) {
-    DCHECK(buffers[i].size() == size_);
-    int32_t id = buffers[i].id();
-    output_picture_buffers_.insert(std::make_pair(id, buffers[i]));
-    free_picture_ids_.push(id);
-
-    picture_buffer_manager_.AssignOnePictureBuffer(buffers[i], have_context);
-  }
-  TRACE_COUNTER1("media", "AVDA::FreePictureIds", free_picture_ids_.size());
-  DoIOTask(true);
-}
-
-void AndroidVideoDecodeAccelerator::ReusePictureBuffer(
-    int32_t picture_buffer_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  free_picture_ids_.push(picture_buffer_id);
-  TRACE_COUNTER1("media", "AVDA::FreePictureIds", free_picture_ids_.size());
-
-  auto it = output_picture_buffers_.find(picture_buffer_id);
-  if (it == output_picture_buffers_.end()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE,
-                 "Can't find PictureBuffer id " << picture_buffer_id);
-    return;
-  }
-
-  picture_buffer_manager_.ReuseOnePictureBuffer(it->second);
-  DoIOTask(true);
-}
-
-void AndroidVideoDecodeAccelerator::Flush() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-  StartCodecDrain(DRAIN_FOR_FLUSH);
-}
-
-void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!media_codec_);
-  DCHECK_NE(state_, WAITING_FOR_CODEC);
-  state_ = WAITING_FOR_CODEC;
-
-  codec_allocator_->CreateMediaCodecAsync(weak_this_factory_.GetWeakPtr(),
-                                          codec_config_);
-}
-
-void AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!media_codec_);
-  DCHECK_NE(state_, WAITING_FOR_CODEC);
-  state_ = WAITING_FOR_CODEC;
-
-  std::unique_ptr<MediaCodecBridge> media_codec =
-      codec_allocator_->CreateMediaCodecSync(codec_config_);
-  OnCodecConfigured(std::move(media_codec), codec_config_->surface_bundle);
-}
-
-void AndroidVideoDecodeAccelerator::OnCodecConfigured(
-    std::unique_ptr<MediaCodecBridge> media_codec,
-    scoped_refptr<AVDASurfaceBundle> surface_bundle) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED);
-  // If we are supposed to notify that initialization is complete, then do so
-  // before returning.  Otherwise, this is a reconfiguration.
-
-  DCHECK(!media_codec_);
-  media_codec_ = std::move(media_codec);
-
-  // If |state_| changed to SURFACE_DESTROYED while we were configuring a codec,
-  // then the codec is already invalid so we return early and drop it.
-  if (state_ == SURFACE_DESTROYED) {
-    if (deferred_initialization_pending_) {
-      // Losing the output surface is not considered an error state, so notify
-      // success. The client will destroy |this| soon.
-      NotifyInitializationSucceeded();
-    }
-
-    // Post it to the right thread.
-    ReleaseCodecAndBundle();
-    return;
-  }
-
-  picture_buffer_manager_.CodecChanged(media_codec_.get());
-  if (!media_codec_) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec");
-    return;
-  }
-
-  if (deferred_initialization_pending_)
-    NotifyInitializationSucceeded();
-
-  state_ = NO_ERROR;
-
-  ManageTimer(true);
-}
-
-void AndroidVideoDecodeAccelerator::StartCodecDrain(DrainType drain_type) {
-  DVLOG(2) << __func__ << " drain_type:" << drain_type;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  auto previous_drain_type = drain_type_;
-  drain_type_ = drain_type;
-
-  // Only DRAIN_FOR_DESTROY is allowed while a drain is already in progress.
-  DCHECK(!previous_drain_type || drain_type == DRAIN_FOR_DESTROY)
-      << "StartCodecDrain(" << drain_type
-      << ") while already draining with type " << previous_drain_type.value();
-
-  // Skip the drain if:
-  // * There's no codec.
-  // * The codec is not currently decoding and we have no more inputs to submit.
-  //   (Reset() and Destroy() should clear pending inputs before calling this).
-  // * The drain is for reset or destroy (where we can drop pending decodes) and
-  //   the codec is not VP8. We still have to drain VP8 in this case because
-  //   MediaCodec can hang in release() or flush() if we don't drain it.
-  //   http://crbug.com/598963
-  if (!media_codec_ ||
-      (pending_bitstream_records_.empty() &&
-       bitstream_buffers_in_decoder_.empty()) ||
-      (drain_type != DRAIN_FOR_FLUSH && codec_config_->codec != kCodecVP8)) {
-    OnDrainCompleted();
-    return;
-  }
-
-  // Queue EOS if one is not already queued.
-  if (!previous_drain_type)
-    DecodeBuffer(BitstreamBuffer(-1, base::SharedMemoryHandle(),
-                                 false /* read_only */, 0));
-}
-
-bool AndroidVideoDecodeAccelerator::IsDrainingForResetOrDestroy() const {
-  return drain_type_ == DRAIN_FOR_RESET || drain_type_ == DRAIN_FOR_DESTROY;
-}
-
-void AndroidVideoDecodeAccelerator::OnDrainCompleted() {
-  DVLOG(2) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // Sometimes MediaCodec returns an EOS buffer even if we didn't queue one.
-  // Consider it an error. http://crbug.com/585959.
-  if (!drain_type_) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Unexpected EOS");
-    return;
-  }
-
-  switch (*drain_type_) {
-    case DRAIN_FOR_FLUSH:
-      ResetCodecState();
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::BindOnce(&AndroidVideoDecodeAccelerator::NotifyFlushDone,
-                         weak_this_factory_.GetWeakPtr()));
-      break;
-    case DRAIN_FOR_RESET:
-      ResetCodecState();
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::BindOnce(&AndroidVideoDecodeAccelerator::NotifyResetDone,
-                         weak_this_factory_.GetWeakPtr()));
-      break;
-    case DRAIN_FOR_DESTROY:
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::BindOnce(&AndroidVideoDecodeAccelerator::ActualDestroy,
-                         weak_this_factory_.GetWeakPtr()));
-      break;
-  }
-  drain_type_.reset();
-}
-
-void AndroidVideoDecodeAccelerator::ResetCodecState() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // If there is already a reset in flight, then that counts.  This can really
-  // only happen if somebody calls Reset.
-  // If the surface is destroyed or we're in an error state there's nothing to
-  // do. Note that BEFORE_OVERLAY_INIT implies that we have no codec, but it's
-  // included for completeness.
-  if (state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED ||
-      state_ == BEFORE_OVERLAY_INIT || state_ == ERROR || !media_codec_) {
-    return;
-  }
-
-  bitstream_buffers_in_decoder_.clear();
-
-  if (pending_input_buf_index_ != -1) {
-    // The data for that index exists in the input buffer, but corresponding
-    // shm block been deleted. Check that it is safe to flush the codec, i.e.
-    // |pending_bitstream_records_| is empty.
-    // TODO(timav): keep shm block for that buffer and remove this restriction.
-    DCHECK(pending_bitstream_records_.empty());
-    pending_input_buf_index_ = -1;
-  }
-
-  // If we've just completed a flush don't reset the codec yet. Instead defer
-  // until the next decode call. This prevents us from unbacking frames that
-  // might be out for display at end of stream.
-  codec_needs_reset_ =
-      (drain_type_ == DRAIN_FOR_FLUSH) || (drain_type_ == DRAIN_FOR_RESET);
-  if (codec_needs_reset_)
-    return;
-
-  // Flush the codec if possible, or create a new one if not.
-  if (!MediaCodecUtil::CodecNeedsFlushWorkaround(media_codec_.get())) {
-    DVLOG(3) << __func__ << " Flushing MediaCodec.";
-    media_codec_->Flush();
-    // Since we just flushed all the output buffers, make sure that nothing is
-    // using them.
-    picture_buffer_manager_.CodecChanged(media_codec_.get());
-  } else {
-    DVLOG(3) << __func__ << " Deleting the MediaCodec and creating a new one.";
-    GetManager()->StopTimer(this);
-    // Release the codec, retain the bundle, and allocate a new codec.  It will
-    // not wait for the old one to finish up with the bundle, which is bad.  It
-    // works (usually) because it ends up allocating the codec on the same
-    // thread as is used to release the old one, so it's serialized anyway.
-    ReleaseCodec();
-    ConfigureMediaCodecAsynchronously();
-  }
-}
-
-void AndroidVideoDecodeAccelerator::Reset() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT0("media", "AVDA::Reset");
-
-  if (defer_surface_creation_) {
-    DCHECK(!media_codec_);
-    DCHECK(pending_bitstream_records_.empty());
-    DCHECK_EQ(state_, BEFORE_OVERLAY_INIT);
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&AndroidVideoDecodeAccelerator::NotifyResetDone,
-                       weak_this_factory_.GetWeakPtr()));
-    return;
-  }
-
-  while (!pending_bitstream_records_.empty()) {
-    int32_t bitstream_buffer_id =
-        pending_bitstream_records_.front().buffer.id();
-    pending_bitstream_records_.pop();
-
-    if (bitstream_buffer_id != -1) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::BindOnce(
-              &AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer,
-              weak_this_factory_.GetWeakPtr(), bitstream_buffer_id));
-    }
-  }
-  TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", 0);
-  bitstreams_notified_in_advance_.clear();
-
-  picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_);
-  StartCodecDrain(DRAIN_FOR_RESET);
-}
-
-void AndroidVideoDecodeAccelerator::SetOverlayInfo(
-    const OverlayInfo& overlay_info) {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (state_ == ERROR)
-    return;
-
-  // Update |config_| to contain the most recent info.  Also save a copy, so
-  // that we can check for duplicate info later.
-  OverlayInfo previous_info = config_.overlay_info;
-  config_.overlay_info = overlay_info;
-
-  // It's possible that we'll receive SetSurface before initializing the surface
-  // chooser.  For example, if we defer surface creation, then we'll signal
-  // success to WMPI before initializing it.  WMPI is then free to change
-  // |surface_id|.  In this case, take no additional action, since |config_| is
-  // up to date.  We'll use it later.
-  if (state_ == BEFORE_OVERLAY_INIT)
-    return;
-
-  // Notify the chooser about the fullscreen state.
-  surface_chooser_helper_.SetIsFullscreen(overlay_info.is_fullscreen);
-
-  // Note that these might be kNoSurfaceID / empty.  In that case, we will
-  // revoke the factory.
-  OverlayInfo::RoutingToken routing_token = overlay_info.routing_token;
-
-  // We don't want to change the factory unless this info has actually changed.
-  // We'll get the same info many times if some other part of the config is now
-  // different, such as fullscreen state.
-  base::Optional<AndroidOverlayFactoryCB> new_factory;
-  if (routing_token != previous_info.routing_token) {
-    if (routing_token && overlay_factory_cb_)
-      new_factory = base::BindRepeating(overlay_factory_cb_, *routing_token);
-  }
-
-  surface_chooser_helper_.UpdateChooserState(new_factory);
-}
-
-void AndroidVideoDecodeAccelerator::Destroy() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  picture_buffer_manager_.Destroy(output_picture_buffers_);
-  client_ = nullptr;
-
-  // We don't want to queue more inputs while draining.
-  base::queue<BitstreamRecord>().swap(pending_bitstream_records_);
-  StartCodecDrain(DRAIN_FOR_DESTROY);
-}
-
-void AndroidVideoDecodeAccelerator::ActualDestroy() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // Note that async codec construction might still be in progress.  In that
-  // case, the codec will be deleted when it completes once we invalidate all
-  // our weak refs.
-  weak_this_factory_.InvalidateWeakPtrs();
-  GetManager()->StopTimer(this);
-  // We only release the codec here, in case codec allocation is in progress.
-  // We don't want to modify |codec_config_|.  Note that the ref will sill be
-  // dropped when it completes, or when we delete |this|.
-  ReleaseCodec();
-
-  delete this;
-}
-
-bool AndroidVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread(
-    const base::WeakPtr<Client>& decode_client,
-    const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) {
-  return false;
-}
-
-const gfx::Size& AndroidVideoDecodeAccelerator::GetSize() const {
-  return size_;
-}
-
-gpu::gles2::ContextGroup* AndroidVideoDecodeAccelerator::GetContextGroup()
-    const {
-  return get_context_group_cb_.Run();
-}
-
-std::unique_ptr<gpu::gles2::AbstractTexture>
-AndroidVideoDecodeAccelerator::CreateAbstractTexture(GLenum target,
-                                                     GLenum internal_format,
-                                                     GLsizei width,
-                                                     GLsizei height,
-                                                     GLsizei depth,
-                                                     int border,
-                                                     GLenum format,
-                                                     GLenum type) {
-  return create_abstract_texture_cb_.Run(target, internal_format, width, height,
-                                         depth, border, format, type);
-}
-
-void AndroidVideoDecodeAccelerator::OnStopUsingOverlayImmediately(
-    AndroidOverlay* overlay) {
-  DVLOG(1) << __func__;
-  TRACE_EVENT0("media", "AVDA::OnStopUsingOverlayImmediately");
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // We cannot get here if we're before surface allocation, since we transition
-  // to WAITING_FOR_CODEC (or NO_ERROR, if sync) when we get the surface without
-  // posting.  If we do ever lose the surface before starting codec allocation,
-  // then we could just update the config to use a TextureOwner and return
-  // without changing state.
-  DCHECK_NE(state_, BEFORE_OVERLAY_INIT);
-
-  // If we're transitioning to |overlay|, then just stop here.  We're not also
-  // using the overlay if we're transitioning to it.
-  if (!!incoming_overlay_ && incoming_overlay_->get() == overlay) {
-    incoming_overlay_.reset();
-    return;
-  }
-
-  // If we have no codec, or if our current config doesn't refer to |overlay|,
-  // then do nothing.  |overlay| might be for some overlay that's waiting for
-  // codec destruction on some other thread.
-  if (!codec_config_->surface_bundle ||
-      codec_config_->surface_bundle->overlay.get() != overlay) {
-    return;
-  }
-
-  // If we have a codec, or if codec allocation is in flight, then it's using an
-  // overlay that was destroyed.
-  if (state_ == WAITING_FOR_CODEC) {
-    // What we should do here is to set |incoming_overlay_| to nullptr, to start
-    // a transistion to TextureOwner.  OnCodecConfigured could notice that
-    // there's an incoming overlay, and then immediately transition the codec /
-    // drop and re-allocate the codec using it.  However, for CVV, that won't
-    // work, since CVV-based overlays block the main thread waiting for the
-    // overlay to be dropped, so OnCodecConfigured won't run.  For DS, it's the
-    // right thing.
-    // So, for now, we just fail, and let OnCodecConfigured drop the codec.
-    // Note that this case really can only happen on pre-M anyway, unless it's
-    // during initial construction.  This will result in the overlay being
-    // destroyed after timeout, since OnCodecConfigured can't run until the
-    // synchronous CVV destruction quits.
-    state_ = SURFACE_DESTROYED;
-    return;
-  }
-
-  // If the API is available avoid having to restart the decoder in order to
-  // leave fullscreen. If we don't clear the surface immediately during this
-  // callback, the MediaCodec will throw an error as the surface is destroyed.
-  if (device_info_->IsSetOutputSurfaceSupported()) {
-    // Since we can't wait for a transition, we must invalidate all outstanding
-    // picture buffers to avoid putting the GL system in a broken state.
-    picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_);
-
-    // If we aren't transitioning to some other surface, then transition to a
-    // TextureOwner.  Remember that, if |incoming_overlay_| is an overlay,
-    // then it's already ready and can be transitioned to immediately.  We were
-    // just waiting for codec buffers to come back, but we just dropped them.
-    // Note that we want |incoming_overlay_| to has_value(), but that value
-    // should be a nullptr to indicate that we should switch to TextureOwner.
-    if (!incoming_overlay_)
-      incoming_overlay_ = std::unique_ptr<AndroidOverlay>();
-
-    UpdateSurface();
-    // Switching to a TextureOwner should never need to wait.  If it does,
-    // then the codec might still be using the destroyed surface, which is bad.
-    return;
-  }
-
-  // If we're currently asynchronously configuring a codec, it will be destroyed
-  // when configuration completes and it notices that |state_| has changed to
-  // SURFACE_DESTROYED.  It's safe to modify |codec_config_| here, since we
-  // checked above for WAITING_FOR_CODEC.
-  state_ = SURFACE_DESTROYED;
-  ReleaseCodecAndBundle();
-
-  // If we're draining, signal completion now because the drain can no longer
-  // proceed.
-  if (drain_type_)
-    OnDrainCompleted();
-}
-
-void AndroidVideoDecodeAccelerator::InitializeCdm() {
-  DVLOG(2) << __func__ << ": " << config_.cdm_id;
-
-#if !BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-  NOTREACHED();
-#else
-  // Store the CDM to hold a reference to it.
-  cdm_for_reference_holding_only_ =
-      CdmManager::GetInstance()->GetCdm(config_.cdm_id);
-
-  // We can DCHECK here and below because we checked HasValidCdm() before
-  // calling InitializeCdm(), and the status shouldn't have changed since then.
-  DCHECK(cdm_for_reference_holding_only_) << "CDM not available";
-
-  media_crypto_context_ =
-      cdm_for_reference_holding_only_->GetCdmContext()->GetMediaCryptoContext();
-  DCHECK(media_crypto_context_) << "MediaCryptoContext not available.";
-
-  // Deferred initialization will continue in OnMediaCryptoReady(). The callback
-  // registered will be posted back to this thread via BindToCurrentLoop.
-  media_crypto_context_->SetMediaCryptoReadyCB(BindToCurrentLoop(
-      base::Bind(&AndroidVideoDecodeAccelerator::OnMediaCryptoReady,
-                 weak_this_factory_.GetWeakPtr())));
-#endif  // !BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-}
-
-void AndroidVideoDecodeAccelerator::OnMediaCryptoReady(
-    JavaObjectPtr media_crypto,
-    bool requires_secure_video_codec) {
-  DVLOG(1) << __func__;
-  DCHECK(media_crypto);
-
-  if (media_crypto->is_null()) {
-    media_crypto_context_->SetMediaCryptoReadyCB(
-        MediaCryptoContext::MediaCryptoReadyCB());
-    media_crypto_context_ = nullptr;
-    cdm_for_reference_holding_only_ = nullptr;
-
-    if (config_.is_encrypted()) {
-      LOG(ERROR)
-          << "MediaCrypto is not available, can't play encrypted stream.";
-      NOTIFY_ERROR(PLATFORM_FAILURE, "MediaCrypto is not available");
-      return;
-    }
-
-    // MediaCrypto is not available, but the stream is clear. So we can still
-    // play the current stream. But if we switch to an encrypted stream playback
-    // will fail.
-    StartSurfaceChooser();
-    return;
-  }
-
-  // We assume this is a part of the initialization process, thus MediaCodec
-  // is not created yet.
-  DCHECK(!media_codec_);
-  DCHECK(deferred_initialization_pending_);
-
-  // Since |this| holds a reference to the |cdm_|, by the time the CDM is
-  // destructed, UnregisterPlayer() must have been called and |this| has been
-  // destructed as well. So the |cdm_unset_cb| will never have a chance to be
-  // called.
-  // TODO(xhwang): Remove |cdm_unset_cb| after it's not used on all platforms.
-  cdm_registration_id_ = media_crypto_context_->RegisterPlayer(
-      BindToCurrentLoop(base::Bind(&AndroidVideoDecodeAccelerator::OnKeyAdded,
-                                   weak_this_factory_.GetWeakPtr())),
-      base::DoNothing());
-
-  codec_config_->media_crypto = std::move(media_crypto);
-  codec_config_->requires_secure_codec = requires_secure_video_codec;
-
-  // Request a secure surface in all cases.  For L3, it's okay if we fall back
-  // to TextureOwner rather than fail composition.  For L1, it's required.
-  // It's also required if the command line says so.
-  surface_chooser_helper_.SetSecureSurfaceMode(
-      requires_secure_video_codec
-          ? SurfaceChooserHelper::SecureSurfaceMode::kRequired
-          : SurfaceChooserHelper::SecureSurfaceMode::kRequested);
-
-  // After receiving |media_crypto_| we can start with surface creation.
-  StartSurfaceChooser();
-}
-
-void AndroidVideoDecodeAccelerator::OnKeyAdded() {
-  DVLOG(1) << __func__;
-
-  // This can also be called before initial surface allocation has completed,
-  // so we might not have a surface / codec yet.  In that case, we'll never
-  // transition to WAITING_FOR_KEY, which is fine.
-  if (state_ == WAITING_FOR_KEY)
-    state_ = NO_ERROR;
-
-  DoIOTask(true);
-}
-
-void AndroidVideoDecodeAccelerator::NotifyInitializationSucceeded() {
-  DCHECK(deferred_initialization_pending_);
-
-  if (client_)
-    client_->NotifyInitializationComplete(true);
-  deferred_initialization_pending_ = false;
-}
-
-void AndroidVideoDecodeAccelerator::NotifyPictureReady(const Picture& picture) {
-  if (client_)
-    client_->PictureReady(picture);
-}
-
-void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer(
-    int input_buffer_id) {
-  if (client_)
-    client_->NotifyEndOfBitstreamBuffer(input_buffer_id);
-}
-
-void AndroidVideoDecodeAccelerator::NotifyFlushDone() {
-  if (client_)
-    client_->NotifyFlushDone();
-}
-
-void AndroidVideoDecodeAccelerator::NotifyResetDone() {
-  if (client_)
-    client_->NotifyResetDone();
-}
-
-void AndroidVideoDecodeAccelerator::NotifyError(Error error) {
-  state_ = ERROR;
-
-  // If we're in the middle of Initialize, then stop.  It will notice |state_|.
-  if (during_initialize_)
-    return;
-
-  // If deferred init is pending, then notify the client that it failed.
-  if (deferred_initialization_pending_) {
-    if (client_)
-      client_->NotifyInitializationComplete(false);
-    deferred_initialization_pending_ = false;
-    return;
-  }
-
-  // We're after all init.  Just signal an error.
-  if (client_)
-    client_->NotifyError(error);
-}
-
-PromotionHintAggregator::NotifyPromotionHintCB
-AndroidVideoDecodeAccelerator::GetPromotionHintCB() {
-  return base::Bind(&AndroidVideoDecodeAccelerator::NotifyPromotionHint,
-                    weak_this_factory_.GetWeakPtr());
-}
-
-void AndroidVideoDecodeAccelerator::NotifyPromotionHint(
-    PromotionHintAggregator::Hint hint) {
-  bool is_using_overlay =
-      codec_config_->surface_bundle && codec_config_->surface_bundle->overlay;
-  surface_chooser_helper_.NotifyPromotionHintAndUpdateChooser(hint,
-                                                              is_using_overlay);
-}
-
-void AndroidVideoDecodeAccelerator::ManageTimer(bool did_work) {
-  bool should_be_running = true;
-
-  base::TimeTicks now = base::TimeTicks::Now();
-  if (!did_work && !most_recent_work_.is_null()) {
-    // Make sure that we have done work recently enough, else stop the timer.
-    if (now - most_recent_work_ > IdleTimerTimeOut) {
-      most_recent_work_ = base::TimeTicks();
-      should_be_running = false;
-    }
-  } else {
-    most_recent_work_ = now;
-  }
-
-  if (should_be_running)
-    GetManager()->StartTimer(this);
-  else
-    GetManager()->StopTimer(this);
-}
-
-// static
-VideoDecodeAccelerator::Capabilities
-AndroidVideoDecodeAccelerator::GetCapabilities(
-    const gpu::GpuPreferences& gpu_preferences) {
-  Capabilities capabilities;
-  SupportedProfiles& profiles = capabilities.supported_profiles;
-
-  if (MediaCodecUtil::IsVp8DecoderAvailable()) {
-    SupportedProfile profile;
-    profile.profile = VP8PROFILE_ANY;
-    // Since there is little to no power benefit below 360p, don't advertise
-    // support for it.  Let libvpx decode it, and save a MediaCodec instance.
-    // Note that we allow it anyway for encrypted content, since we push a
-    // separate profile for that.
-    profile.min_resolution.SetSize(480, 360);
-    profile.max_resolution.SetSize(3840, 2160);
-    // If we know MediaCodec will just create a software codec, prefer our
-    // internal software decoder instead. It's more up to date and secured
-    // within the renderer sandbox. However if the content is encrypted, we
-    // must use MediaCodec anyways since MediaDrm offers no way to decrypt
-    // the buffers and let us use our internal software decoders.
-    profile.encrypted_only = MediaCodecUtil::IsKnownUnaccelerated(
-        kCodecVP8, MediaCodecDirection::DECODER);
-    profiles.push_back(profile);
-
-    // Always allow encrypted content, even at low resolutions.
-    profile.min_resolution.SetSize(0, 0);
-    profile.encrypted_only = true;
-    profiles.push_back(profile);
-  }
-
-  if (MediaCodecUtil::IsVp9DecoderAvailable()) {
-    const VideoCodecProfile profile_types[] = {
-        VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE1, VP9PROFILE_PROFILE2,
-        VP9PROFILE_PROFILE3, VIDEO_CODEC_PROFILE_UNKNOWN};
-    const bool is_known_unaccelerated = MediaCodecUtil::IsKnownUnaccelerated(
-        kCodecVP9, MediaCodecDirection::DECODER);
-    for (int i = 0; profile_types[i] != VIDEO_CODEC_PROFILE_UNKNOWN; i++) {
-      SupportedProfile profile;
-      // Limit to 360p, like we do for vp8.  See above.
-      profile.min_resolution.SetSize(480, 360);
-      profile.max_resolution.SetSize(3840, 2160);
-      // If we know MediaCodec will just create a software codec, prefer our
-      // internal software decoder instead. It's more up to date and secured
-      // within the renderer sandbox. However if the content is encrypted, we
-      // must use MediaCodec anyways since MediaDrm offers no way to decrypt
-      // the buffers and let us use our internal software decoders.
-      profile.encrypted_only = is_known_unaccelerated;
-      profile.profile = profile_types[i];
-      profiles.push_back(profile);
-
-      // Always allow encrypted content.
-      profile.min_resolution.SetSize(0, 0);
-      profile.encrypted_only = true;
-      profiles.push_back(profile);
-    }
-  }
-
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-  for (const auto& supported_profile : kSupportedH264Profiles) {
-    SupportedProfile profile;
-    profile.profile = supported_profile;
-    profile.min_resolution.SetSize(0, 0);
-    // Advertise support for 4k and let the MediaCodec fail when decoding if it
-    // doesn't support the resolution. It's assumed that consumers won't have
-    // software fallback for H264 on Android anyway.
-    profile.max_resolution.SetSize(3840, 2160);
-    profiles.push_back(profile);
-  }
-
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
-  for (const auto& supported_profile : kSupportedHevcProfiles) {
-    SupportedProfile profile;
-    profile.profile = supported_profile;
-    profile.min_resolution.SetSize(0, 0);
-    profile.max_resolution.SetSize(3840, 2160);
-    profiles.push_back(profile);
-  }
-#endif
-#endif
-
-  capabilities.flags = Capabilities::SUPPORTS_DEFERRED_INITIALIZATION |
-                       Capabilities::NEEDS_ALL_PICTURE_BUFFERS_TO_DECODE |
-                       Capabilities::SUPPORTS_ENCRYPTED_STREAMS;
-
-  // If we're using threaded texture mailboxes the COPY_REQUIRED flag must be
-  // set on the video frames (http://crbug.com/582170), and SurfaceView output
-  // is disabled (http://crbug.com/582170).
-  if (gpu_preferences.enable_threaded_texture_mailboxes) {
-    capabilities.flags |= Capabilities::REQUIRES_TEXTURE_COPY;
-  } else if (MediaCodecUtil::IsSurfaceViewOutputSupported()) {
-    capabilities.flags |= Capabilities::SUPPORTS_EXTERNAL_OUTPUT_SURFACE;
-    if (MediaCodecUtil::IsSetOutputSurfaceSupported())
-      capabilities.flags |= Capabilities::SUPPORTS_SET_EXTERNAL_OUTPUT_SURFACE;
-  }
-
-  return capabilities;
-}
-
-bool AndroidVideoDecodeAccelerator::IsMediaCodecSoftwareDecodingForbidden()
-    const {
-  // Prevent MediaCodec from using its internal software decoders when we have
-  // more secure and up to date versions in the renderer process.
-  return !config_.is_encrypted() &&
-         (codec_config_->codec == kCodecVP8 ||
-          codec_config_->codec == kCodecVP9) &&
-         !force_allow_software_decoding_for_testing_;
-}
-
-bool AndroidVideoDecodeAccelerator::UpdateSurface() {
-  DCHECK(incoming_overlay_);
-  DCHECK_NE(state_, WAITING_FOR_CODEC);
-
-  // Start surface creation.  Note that if we're called via surfaceDestroyed,
-  // then this must complete synchronously or it will DCHECK.  Otherwise, we
-  // might still be using the destroyed surface.  We don't enforce this, but
-  // it's worth remembering that there are cases where it's required.
-  // Note that we don't re-use |surface_bundle|, since the codec is using it!
-  incoming_bundle_ =
-      new AVDASurfaceBundle(std::move(incoming_overlay_.value()));
-  incoming_overlay_.reset();
-  InitializePictureBufferManager();
-  if (state_ == ERROR) {
-    // This might be called from OnSurfaceDestroyed(), so we have to release the
-    // MediaCodec if we failed to switch the surface.  We reset the surface ID
-    // to the previous one, since failures never result in the codec using the
-    // new surface.  This is only guaranteed because of how OnCodecConfigured
-    // works.  If it could fail after getting a codec, then this assumption
-    // wouldn't be necessarily true anymore.
-    // Also note that we might not have switched surfaces yet, which is also bad
-    // for OnSurfaceDestroyed, because of BEFORE_OVERLAY_INIT.  Shouldn't
-    // happen with TextureOwner, and OnSurfaceDestroyed checks for it.  In
-    // either case, we definitely should not still have an incoming bundle; it
-    // should have been dropped.
-    DCHECK(!incoming_bundle_);
-    ReleaseCodecAndBundle();
-  }
-
-  return state_ != ERROR;
-}
-
-void AndroidVideoDecodeAccelerator::ReleaseCodec() {
-  if (!media_codec_)
-    return;
-
-  picture_buffer_manager_.CodecChanged(nullptr);
-  codec_allocator_->ReleaseMediaCodec(std::move(media_codec_),
-                                      codec_config_->surface_bundle);
-}
-
-void AndroidVideoDecodeAccelerator::ReleaseCodecAndBundle() {
-  ReleaseCodec();
-  codec_config_->surface_bundle = nullptr;
-}
-
-void AndroidVideoDecodeAccelerator::CacheFrameInformation() {
-  bool is_using_overlay =
-      codec_config_->surface_bundle && codec_config_->surface_bundle->overlay;
-
-  cached_frame_information_ =
-      surface_chooser_helper_.ComputeFrameInformation(is_using_overlay);
-}
-
-}  // namespace media
diff --git a/media/gpu/android/android_video_decode_accelerator.h b/media/gpu/android/android_video_decode_accelerator.h
deleted file mode 100644
index d338828..0000000
--- a/media/gpu/android/android_video_decode_accelerator.h
+++ /dev/null
@@ -1,429 +0,0 @@
-// Copyright (c) 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 MEDIA_GPU_ANDROID_ANDROID_VIDEO_DECODE_ACCELERATOR_H_
-#define MEDIA_GPU_ANDROID_ANDROID_VIDEO_DECODE_ACCELERATOR_H_
-
-#include <stdint.h>
-
-#include <list>
-#include <map>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/containers/queue.h"
-#include "base/optional.h"
-#include "base/threading/thread_checker.h"
-#include "base/timer/timer.h"
-#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
-#include "gpu/config/gpu_preferences.h"
-#include "media/base/android/media_codec_bridge_impl.h"
-#include "media/base/android/media_crypto_context.h"
-#include "media/base/android_overlay_mojo_factory.h"
-#include "media/base/content_decryption_module.h"
-#include "media/gpu/android/avda_picture_buffer_manager.h"
-#include "media/gpu/android/avda_state_provider.h"
-#include "media/gpu/android/codec_allocator.h"
-#include "media/gpu/android/device_info.h"
-#include "media/gpu/android/surface_chooser_helper.h"
-#include "media/gpu/gpu_video_decode_accelerator_helpers.h"
-#include "media/gpu/media_gpu_export.h"
-#include "media/video/video_decode_accelerator.h"
-#include "ui/gl/android/scoped_java_surface.h"
-#include "ui/gl/android/surface_texture.h"
-
-namespace media {
-class AndroidVideoSurfaceChooser;
-class PromotionHintAggregator;
-
-// A VideoDecodeAccelerator implementation for Android. This class decodes the
-// encoded input stream using Android's MediaCodec. It handles the work of
-// transferring data to and from MediaCodec, and delegates attaching MediaCodec
-// output buffers to PictureBuffers to AVDAPictureBufferManager.
-class MEDIA_GPU_EXPORT AndroidVideoDecodeAccelerator
-    : public VideoDecodeAccelerator,
-      public AVDAStateProvider,
-      public CodecAllocatorClient {
- public:
-  static VideoDecodeAccelerator::Capabilities GetCapabilities(
-      const gpu::GpuPreferences& gpu_preferences);
-
-  AndroidVideoDecodeAccelerator(
-      CodecAllocator* codec_allocator,
-      std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser,
-      const MakeGLContextCurrentCallback& make_context_current_cb,
-      const GetContextGroupCallback& get_context_group_cb,
-      const AndroidOverlayMojoFactoryCB& overlay_factory_cb,
-      const CreateAbstractTextureCallback& create_abstract_texture_cb,
-      DeviceInfo* device_info);
-
-  ~AndroidVideoDecodeAccelerator() override;
-
-  // VideoDecodeAccelerator implementation:
-  bool Initialize(const Config& config, Client* client) override;
-  void Decode(BitstreamBuffer bitstream_buffer) override;
-  void AssignPictureBuffers(const std::vector<PictureBuffer>& buffers) override;
-  void ReusePictureBuffer(int32_t picture_buffer_id) override;
-  void Flush() override;
-  void Reset() override;
-  void SetOverlayInfo(const OverlayInfo& overlay_info) override;
-  void Destroy() override;
-  bool TryToSetupDecodeOnSeparateThread(
-      const base::WeakPtr<Client>& decode_client,
-      const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner)
-      override;
-
-  // AVDAStateProvider implementation:
-  const gfx::Size& GetSize() const override;
-  gpu::gles2::ContextGroup* GetContextGroup() const override;
-  std::unique_ptr<gpu::gles2::AbstractTexture> CreateAbstractTexture(
-      GLenum target,
-      GLenum internal_format,
-      GLsizei width,
-      GLsizei height,
-      GLsizei depth,
-      int border,
-      GLenum format,
-      GLenum type) override;
-  // Notifies the client about the error and sets |state_| to |ERROR|.  If we're
-  // in the middle of Initialize, we guarantee that Initialize will return
-  // failure.  If deferred init is pending, then we'll fail deferred init.
-  // Otherwise, we'll signal errors normally.
-  void NotifyError(Error error) override;
-  PromotionHintAggregator::NotifyPromotionHintCB GetPromotionHintCB() override;
-
-  // CodecAllocatorClient implementation:
-  void OnCodecConfigured(
-      std::unique_ptr<MediaCodecBridge> media_codec,
-      scoped_refptr<AVDASurfaceBundle> surface_bundle) override;
-
- private:
-  friend class AVDAManager;
-
-  // TODO(timav): evaluate the need for more states in the AVDA state machine.
-  enum State {
-    NO_ERROR,
-    ERROR,
-    // We haven't initialized |surface_chooser_| yet, so we don't have a surface
-    // or a codec.  After we initialize |surface_chooser_|, we'll transition to
-    // WAITING_FOR_CODEC, NO_ERROR, or ERROR.
-    BEFORE_OVERLAY_INIT,
-    // Set when we are asynchronously constructing the codec.  Will transition
-    // to NO_ERROR or ERROR depending on success.
-    WAITING_FOR_CODEC,
-    // Set when we have a codec, but it doesn't yet have a key.
-    WAITING_FOR_KEY,
-    // The output surface was destroyed. We must not configure a new MediaCodec
-    // with the destroyed surface.
-    SURFACE_DESTROYED,
-  };
-
-  enum DrainType {
-    DRAIN_FOR_FLUSH,
-    DRAIN_FOR_RESET,
-    DRAIN_FOR_DESTROY,
-  };
-
-  // Called once before (possibly deferred) initialization succeeds, to set up
-  // |surface_chooser_| with our initial factory from VDA::Config.
-  void StartSurfaceChooser();
-
-  // Start a transition to an overlay, or, if |!overlay|, TextureOwner.  The
-  // transition doesn't have to be immediate; we'll favor not dropping frames.
-  void OnSurfaceTransition(std::unique_ptr<AndroidOverlay> overlay);
-
-  // Called by AndroidOverlay when a surface is lost.  We will discard pending
-  // frames, as needed, to switch away from |overlay| if we're using it.  Before
-  // we return, we will have either dropped |overlay| if we own it, or posted
-  // it for async release with the codec that's using it.  We also handle the
-  // case where we're not using |overlay| at all, since that can happen too
-  // while async codec release is pending.
-  void OnStopUsingOverlayImmediately(AndroidOverlay* overlay);
-
-  // Initializes the picture buffer manager to use the current surface, once
-  // it is available.  This is not normally called directly, but rather via
-  // StartSurfaceCreation.  If we have a media codec already, then this will
-  // attempt to setSurface the new surface.  Otherwise, it will start codec
-  // config using the new surface.  In that case, there might not be a codec
-  // ready even if this succeeds, but async config will be started.  If
-  // setSurface fails, this will not replace the codec.  On failure, this will
-  // transition |state_| to ERROR.
-  // Note that this assumes that there is an |incoming_bundle_| that we'll use.
-  // On success, we'll replace the bundle in |codec_config_|.  On failure, we'll
-  // delete the incoming bundle.
-  void InitializePictureBufferManager();
-
-  // A part of destruction process that is sometimes postponed after the drain.
-  void ActualDestroy();
-
-  // Configures |media_codec_| with the given codec parameters from the client.
-  // This configuration will (probably) not be complete before this call
-  // returns.  Multiple calls before completion will be ignored.  |state_|
-  // must be NO_ERROR or WAITING_FOR_CODEC.  Note that, once you call this,
-  // you should be careful to avoid modifying members of |codec_config_| until
-  // |state_| is no longer WAITING_FOR_CODEC.
-  void ConfigureMediaCodecAsynchronously();
-
-  // Like ConfigureMediaCodecAsynchronously, but synchronous.  Will NotifyError
-  // on failure.  Since all configuration is done synchronously, there is no
-  // concern with modifying |codec_config_| after this returns.
-  void ConfigureMediaCodecSynchronously();
-
-  // Sends the decoded frame specified by |codec_buffer_index| to the client.
-  void SendDecodedFrameToClient(int32_t codec_buffer_index,
-                                int32_t bitstream_id);
-
-  // Does pending IO tasks if any. Once this is called, it polls |media_codec_|
-  // until it finishes pending tasks. For the polling, |kDecodePollDelay| is
-  // used.
-  void DoIOTask(bool start_timer);
-
-  // Feeds buffers in |pending_bitstream_records_| to |media_codec_|. Returns
-  // true if one was queued.
-  bool QueueInput();
-
-  // Dequeues output from |media_codec_| and feeds the decoded frame to the
-  // client.  Returns a hint about whether calling again might produce
-  // more output.
-  bool DequeueOutput();
-
-  // Requests picture buffers from the client.
-  void RequestPictureBuffers();
-
-  // Decode the content in the |bitstream_buffer|. Note that a
-  // |bitstream_buffer| of id as -1 indicates a flush command.
-  void DecodeBuffer(BitstreamBuffer bitstream_buffer);
-
-  // Called during Initialize() for encrypted streams to set up the CDM.
-  void InitializeCdm();
-
-  // Called after the CDM obtains a MediaCrypto object.
-  void OnMediaCryptoReady(JavaObjectPtr media_crypto,
-                          bool requires_secure_video_codec);
-
-  // Called when a new key is added to the CDM.
-  void OnKeyAdded();
-
-  // Notifies the client that deferred initialization succeeded.  If it fails,
-  // then call NotifyError instead.
-  void NotifyInitializationSucceeded();
-
-  // Notifies the client about the availability of a picture.
-  void NotifyPictureReady(const Picture& picture);
-
-  // Notifies the client that the input buffer identifed by input_buffer_id has
-  // been processed.
-  void NotifyEndOfBitstreamBuffer(int input_buffer_id);
-
-  // Notifies the client that the decoder was flushed.
-  void NotifyFlushDone();
-
-  // Notifies the client that the decoder was reset.
-  void NotifyResetDone();
-
-  // Start or stop our work-polling timer based on whether we did any work, and
-  // how long it has been since we've done work.  Calling this with true will
-  // start the timer.  Calling it with false may stop the timer.
-  void ManageTimer(bool did_work);
-
-  // Start the MediaCodec drain process by adding end_of_stream() buffer to the
-  // encoded buffers queue. When we receive EOS from the output buffer the drain
-  // process completes and we perform the action depending on the |drain_type|.
-  void StartCodecDrain(DrainType drain_type);
-
-  // Returns true if we are currently draining the codec and doing that as part
-  // of Reset() or Destroy() VP8 workaround. (http://crbug.com/598963). We won't
-  // display any frames and disable normal errors handling.
-  bool IsDrainingForResetOrDestroy() const;
-
-  // A helper method that performs the operation required after the drain
-  // completion (usually when we receive EOS in the output). The operation
-  // itself depends on the |drain_type_|.
-  void OnDrainCompleted();
-
-  // Resets MediaCodec and buffers/containers used for storing output. These
-  // components need to be reset upon EOS to decode a later stream. Input state
-  // (e.g. queued BitstreamBuffers) is not reset, as input following an EOS
-  // is still valid and should be processed.
-  void ResetCodecState();
-
-  // Indicates if MediaCodec should not be used for software decoding since we
-  // have safer versions elsewhere.
-  bool IsMediaCodecSoftwareDecodingForbidden() const;
-
-  // On platforms which support seamless surface changes, this will reinitialize
-  // the picture buffer manager with the new surface. This function reads and
-  // clears the surface id from |pending_surface_id_|. It will issue a decode
-  // error if the surface change fails. Returns false on failure.
-  bool UpdateSurface();
-
-  // Release |media_codec_| if it's not null, and notify
-  // |picture_buffer_manager_|.
-  void ReleaseCodec();
-
-  // ReleaseCodec(), and also drop our ref to it's surface bundle.  This is
-  // the right thing to do unless you're planning to re-use the bundle with
-  // another codec.  Normally, one doesn't.
-  void ReleaseCodecAndBundle();
-
-  // Send a |hint| to |promotion_hint_aggregator_|.
-  void NotifyPromotionHint(PromotionHintAggregator::Hint hint);
-
-  // Used to DCHECK that we are called on the correct thread.
-  base::ThreadChecker thread_checker_;
-
-  // To expose client callbacks from VideoDecodeAccelerator.
-  Client* client_;
-
-  CodecAllocator* codec_allocator_;
-
-  // Callback to set the correct gl context.
-  MakeGLContextCurrentCallback make_context_current_cb_;
-
-  // Callback to get the ContextGroup*.
-  GetContextGroupCallback get_context_group_cb_;
-
-  // The current state of this class. For now, this is used only for setting
-  // error state.
-  State state_;
-
-  // The assigned picture buffers by picture buffer id.
-  AVDAPictureBufferManager::PictureBufferMap output_picture_buffers_;
-
-  // This keeps the free picture buffer ids which can be used for sending
-  // decoded frames to the client.
-  base::queue<int32_t> free_picture_ids_;
-
-  // The low-level decoder which Android SDK provides.
-  std::unique_ptr<MediaCodecBridge> media_codec_;
-
-  // Set to true after requesting picture buffers to the client.
-  bool picturebuffers_requested_;
-
-  // The resolution of the stream.
-  gfx::Size size_;
-
-  // Handy structure to remember a BitstreamBuffer and also its shared memory,
-  // if any.  The goal is to prevent leaving a BitstreamBuffer's shared memory
-  // handle open.
-  struct BitstreamRecord {
-    BitstreamRecord(BitstreamBuffer);
-    BitstreamRecord(BitstreamRecord&& other);
-    ~BitstreamRecord();
-
-    // The region in this buffer will not be valid, as it will have been passed
-    // to |memory|, below.
-    BitstreamBuffer buffer;
-
-    // |memory| may be null if buffer has no data.
-    std::unique_ptr<UnalignedSharedMemory> memory;
-  };
-
-  // Encoded bitstream buffers to be passed to media codec, queued until an
-  // input buffer is available.
-  base::queue<BitstreamRecord> pending_bitstream_records_;
-
-  // A map of presentation timestamp to bitstream buffer id for the bitstream
-  // buffers that have been submitted to the decoder but haven't yet produced an
-  // output frame with the same timestamp. Note: there will only be one entry
-  // for multiple bitstream buffers that have the same presentation timestamp.
-  std::map<base::TimeDelta, int32_t> bitstream_buffers_in_decoder_;
-
-  // Keeps track of bitstream ids notified to the client with
-  // NotifyEndOfBitstreamBuffer() before getting output from the bitstream.
-  std::list<int32_t> bitstreams_notified_in_advance_;
-
-  AVDAPictureBufferManager picture_buffer_manager_;
-
-  // Time at which we last did useful work on io_timer_.
-  base::TimeTicks most_recent_work_;
-
-  // The ongoing drain operation, if any.
-  base::Optional<DrainType> drain_type_;
-
-  // Holds a ref-count to the CDM to avoid using the CDM after it's destroyed.
-  scoped_refptr<ContentDecryptionModule> cdm_for_reference_holding_only_;
-
-  // Owned by CDM which is external to this decoder.
-  MediaCryptoContext* media_crypto_context_;
-
-  // MediaDrmBridge requires registration/unregistration of the player, this
-  // registration id is used for this.
-  int cdm_registration_id_;
-
-  // Configuration that we use for MediaCodec.
-  // Do not update any of its members while |state_| is WAITING_FOR_CODEC.
-  scoped_refptr<CodecConfig> codec_config_;
-
-  // Index of the dequeued and filled buffer that we keep trying to enqueue.
-  // Such buffer appears in MEDIA_CODEC_NO_KEY processing.
-  int pending_input_buf_index_;
-
-  // Monotonically increasing value that is used to prevent old, delayed errors
-  // from being sent after a reset.
-  int error_sequence_token_;
-
-  // Are we currently processing a call to Initialize()?  Please don't use this
-  // unless you're NotifyError.
-  bool during_initialize_;
-
-  // True if and only if VDA initialization is deferred, and we have not yet
-  // called NotifyInitializationComplete.
-  bool deferred_initialization_pending_;
-
-  // Indicates if ResetCodecState() should be called upon the next call to
-  // Decode(). Allows us to avoid trashing the last few frames of a playback
-  // when the EOS buffer is received.
-  bool codec_needs_reset_;
-
-  // True if surface creation and |picture_buffer_manager_| initialization has
-  // been defered until the first Decode() call.
-  bool defer_surface_creation_;
-
-  // Copy of the VDA::Config we were given.
-  Config config_;
-
-  // SurfaceBundle that we're going to use for StartSurfaceCreation.  This is
-  // separate than the bundle in |codec_config_|, since we can start surface
-  // creation while another codec is using the old surface.  For example, if
-  // we're going to SetSurface, then the current codec will depend on the
-  // current bundle until then.
-  scoped_refptr<AVDASurfaceBundle> incoming_bundle_;
-
-  // If we have been given an overlay to use, then this is it.  If we've been
-  // told to move to TextureOwner, then this will be value() == nullptr.
-  base::Optional<std::unique_ptr<AndroidOverlay>> incoming_overlay_;
-
-  SurfaceChooserHelper surface_chooser_helper_;
-
-  DeviceInfo* device_info_;
-
-  bool force_defer_surface_creation_for_testing_;
-
-  bool force_allow_software_decoding_for_testing_;
-
-  // Optional factory to produce mojo AndroidOverlay instances.
-  AndroidOverlayMojoFactoryCB overlay_factory_cb_;
-
-  CreateAbstractTextureCallback create_abstract_texture_cb_;
-
-  std::unique_ptr<PromotionHintAggregator> promotion_hint_aggregator_;
-
-  // Update |cached_frame_information_|.
-  void CacheFrameInformation();
-
-  // Most recently cached frame information, so that we can dispatch it without
-  // recomputing it on every frame.  It changes very rarely.
-  SurfaceChooserHelper::FrameInformation cached_frame_information_ =
-      SurfaceChooserHelper::FrameInformation::NON_OVERLAY_INSECURE;
-
-  // WeakPtrFactory for posting tasks back to |this|.
-  base::WeakPtrFactory<AndroidVideoDecodeAccelerator> weak_this_factory_;
-
-  friend class AndroidVideoDecodeAcceleratorTest;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_ANDROID_ANDROID_VIDEO_DECODE_ACCELERATOR_H_
diff --git a/media/gpu/android/android_video_decode_accelerator_unittest.cc b/media/gpu/android/android_video_decode_accelerator_unittest.cc
deleted file mode 100644
index 2f7c25e..0000000
--- a/media/gpu/android/android_video_decode_accelerator_unittest.cc
+++ /dev/null
@@ -1,603 +0,0 @@
-// Copyright (c) 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 "media/gpu/android/android_video_decode_accelerator.h"
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/weak_ptr.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "gpu/command_buffer/client/client_test_helper.h"
-#include "gpu/command_buffer/service/context_group.h"
-#include "gpu/command_buffer/service/gpu_tracer.h"
-#include "gpu/command_buffer/service/image_manager.h"
-#include "gpu/command_buffer/service/mailbox_manager_impl.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
-#include "gpu/command_buffer/service/shared_image_manager.h"
-#include "media/base/android/media_codec_util.h"
-#include "media/base/android/mock_android_overlay.h"
-#include "media/base/android/mock_media_codec_bridge.h"
-#include "media/gpu/android/android_video_decode_accelerator.h"
-#include "media/gpu/android/android_video_surface_chooser.h"
-#include "media/gpu/android/codec_allocator.h"
-#include "media/gpu/android/fake_codec_allocator.h"
-#include "media/gpu/android/mock_abstract_texture.h"
-#include "media/gpu/android/mock_android_video_surface_chooser.h"
-#include "media/gpu/android/mock_device_info.h"
-#include "media/media_buildflags.h"
-#include "media/video/picture.h"
-#include "media/video/video_decode_accelerator.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gl/android/surface_texture.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_surface.h"
-#include "ui/gl/init/gl_factory.h"
-
-using ::testing::NiceMock;
-using ::testing::NotNull;
-using ::testing::Return;
-using ::testing::_;
-
-namespace media {
-namespace {
-
-#define SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE()     \
-  do {                                            \
-    if (!MediaCodecUtil::IsMediaCodecAvailable()) \
-      return;                                     \
-  } while (false)
-
-bool MakeContextCurrent() {
-  return true;
-}
-
-gpu::gles2::ContextGroup* GetContextGroup(
-    scoped_refptr<gpu::gles2::ContextGroup> context_group) {
-  return context_group.get();
-}
-
-class MockVDAClient : public VideoDecodeAccelerator::Client {
- public:
-  MockVDAClient() {}
-
-  MOCK_METHOD1(NotifyInitializationComplete, void(bool));
-  MOCK_METHOD5(
-      ProvidePictureBuffers,
-      void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t));
-  MOCK_METHOD1(DismissPictureBuffer, void(int32_t));
-  MOCK_METHOD1(PictureReady, void(const Picture&));
-  MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t));
-  MOCK_METHOD0(NotifyFlushDone, void());
-  MOCK_METHOD0(NotifyResetDone, void());
-  MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockVDAClient);
-};
-
-}  // namespace
-
-class AndroidVideoDecodeAcceleratorTest
-    : public testing::TestWithParam<VideoCodecProfile> {
- public:
-  // Default to baseline H264 because it's always supported.
-  AndroidVideoDecodeAcceleratorTest() : config_(GetParam()) {}
-
-  void SetUp() override {
-    ASSERT_TRUE(gl::init::InitializeGLOneOff());
-    surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(16, 16));
-    context_ = gl::init::CreateGLContext(nullptr, surface_.get(),
-                                         gl::GLContextAttribs());
-    context_->MakeCurrent(surface_.get());
-
-    codec_allocator_ = std::make_unique<FakeCodecAllocator>(
-        base::SequencedTaskRunnerHandle::Get());
-    device_info_ = std::make_unique<NiceMock<MockDeviceInfo>>();
-
-    chooser_that_is_usually_null_ =
-        std::make_unique<NiceMock<MockAndroidVideoSurfaceChooser>>();
-    chooser_ = chooser_that_is_usually_null_.get();
-
-    feature_info_ = new gpu::gles2::FeatureInfo();
-    context_group_ = new gpu::gles2::ContextGroup(
-        gpu_preferences_, false, &mailbox_manager_, nullptr, nullptr, nullptr,
-        feature_info_, false, &image_manager_, nullptr, nullptr,
-        gpu::GpuFeatureInfo(), &discardable_manager_, nullptr,
-        &shared_image_manager_);
-
-    // By default, allow deferred init.
-    config_.is_deferred_initialization_allowed = true;
-  }
-
-  ~AndroidVideoDecodeAcceleratorTest() override {
-    // ~AVDASurfaceBundle() might rely on GL being available, so we have to
-    // explicitly drop references to them before tearing down GL.
-    vda_ = nullptr;
-    codec_allocator_ = nullptr;
-    context_ = nullptr;
-    surface_ = nullptr;
-    feature_info_ = nullptr;
-    context_group_ = nullptr;
-
-    gl::init::ShutdownGL(false);
-  }
-
-  std::unique_ptr<AndroidOverlay> OverlayFactory(const base::UnguessableToken&,
-                                                 AndroidOverlayConfig config) {
-    // This shouldn't be called by AVDA.  Our mock surface chooser won't use it
-    // either, though it'd be nice to check to token.  Note that this isn't the
-    // same as an emtpy factory callback; that means "no factory".  This one
-    // looks like a working factory, as long as nobody calls it.
-    return nullptr;
-  }
-
-  // Create and initialize AVDA with |config_|, and return the result.
-  bool InitializeAVDA(bool force_defer_surface_creation = false) {
-    // Because VDA has a custom deleter, we must assign it to |vda_| carefully.
-    AndroidVideoDecodeAccelerator* avda = new AndroidVideoDecodeAccelerator(
-        codec_allocator_.get(), std::move(chooser_that_is_usually_null_),
-        base::BindRepeating(&MakeContextCurrent),
-        base::BindRepeating(&GetContextGroup, context_group_),
-        base::BindRepeating(&AndroidVideoDecodeAcceleratorTest::OverlayFactory,
-                            base::Unretained(this)),
-        base::BindRepeating(
-            &AndroidVideoDecodeAcceleratorTest::CreateAbstractTexture,
-            base::Unretained(this)),
-        device_info_.get());
-    vda_.reset(avda);
-    avda->force_defer_surface_creation_for_testing_ =
-        force_defer_surface_creation;
-    avda->force_allow_software_decoding_for_testing_ = true;
-
-    bool result = vda_->Initialize(config_, &client_);
-    base::RunLoop().RunUntilIdle();
-    return result;
-  }
-
-  std::unique_ptr<gpu::gles2::AbstractTexture> CreateAbstractTexture(
-      GLenum target,
-      GLenum internal_format,
-      GLsizei width,
-      GLsizei height,
-      GLsizei depth,
-      int border,
-      GLenum format,
-      GLenum type) {
-    return std::make_unique<MockAbstractTexture>(0);
-  }
-
-  // Initialize |vda_|, providing a new surface for it.  You may get the surface
-  // by asking |codec_allocator_|.
-  void InitializeAVDAWithOverlay() {
-    config_.overlay_info.routing_token = base::UnguessableToken::Create();
-    ASSERT_TRUE(InitializeAVDA());
-    base::RunLoop().RunUntilIdle();
-    ASSERT_TRUE(chooser_->factory_);
-
-    // Have the factory provide an overlay, and verify that codec creation is
-    // provided with that overlay.
-    std::unique_ptr<MockAndroidOverlay> overlay =
-        std::make_unique<MockAndroidOverlay>();
-    overlay_callbacks_ = overlay->GetCallbacks();
-
-    // Set the expectations first, since ProvideOverlay might cause callbacks.
-    EXPECT_CALL(*codec_allocator_,
-                MockCreateMediaCodecAsync(overlay.get(), nullptr));
-    chooser_->ProvideOverlay(std::move(overlay));
-
-    // Provide the codec so that we can check if it's freed properly.
-    EXPECT_CALL(client_, NotifyInitializationComplete(true));
-    codec_allocator_->ProvideMockCodecAsync();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void InitializeAVDAWithTextureOwner() {
-    ASSERT_TRUE(InitializeAVDA());
-    base::RunLoop().RunUntilIdle();
-    // We do not expect a factory, since we are using TextureOwner.
-    ASSERT_FALSE(chooser_->factory_);
-
-    // Set the expectations first, since ProvideOverlay might cause callbacks.
-    EXPECT_CALL(*codec_allocator_,
-                MockCreateMediaCodecAsync(nullptr, NotNull()));
-    chooser_->ProvideTextureOwner();
-
-    // Provide the codec so that we can check if it's freed properly.
-    EXPECT_CALL(client_, NotifyInitializationComplete(true));
-    codec_allocator_->ProvideMockCodecAsync();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  // Set whether HasUnrendereredPictureBuffers will return true or false.
-  // TODO(liberato): We can't actually do this yet.  It turns out to be okay,
-  // because AVDA doesn't actually SetSurface before DequeueOutput.  It could do
-  // so, though, if there aren't unrendered buffers.  Should AVDA ever start
-  // switching surfaces immediately upon receiving them, rather than waiting for
-  // DequeueOutput, then we'll want to be able to indicate that it has
-  // unrendered pictures to prevent that behavior.
-  void SetHasUnrenderedPictureBuffers(bool flag) {}
-
-  // Tell |avda_| to switch surfaces to its incoming surface.  This is a method
-  // since we're a friend of AVDA, and the tests are subclasses.  It's also
-  // somewhat hacky, but much less hacky than trying to run it via a timer.
-  void LetAVDAUpdateSurface() {
-    SetHasUnrenderedPictureBuffers(false);
-    avda()->DequeueOutput();
-  }
-
-  // So that SequencedTaskRunnerHandle::Get() works.
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  scoped_refptr<gl::GLSurface> surface_;
-  scoped_refptr<gl::GLContext> context_;
-  NiceMock<MockVDAClient> client_;
-  std::unique_ptr<FakeCodecAllocator> codec_allocator_;
-
-  scoped_refptr<gpu::gles2::ContextGroup> context_group_;
-  scoped_refptr<gpu::gles2::FeatureInfo> feature_info_;
-  gpu::GpuPreferences gpu_preferences_;
-  gpu::gles2::MailboxManagerImpl mailbox_manager_;
-  gpu::gles2::ImageManager image_manager_;
-  gpu::ServiceDiscardableManager discardable_manager_;
-  gpu::SharedImageManager shared_image_manager_;
-
-  // Only set until InitializeAVDA() is called.
-  std::unique_ptr<MockAndroidVideoSurfaceChooser> chooser_that_is_usually_null_;
-  MockAndroidVideoSurfaceChooser* chooser_;
-  VideoDecodeAccelerator::Config config_;
-  std::unique_ptr<MockDeviceInfo> device_info_;
-
-  // Set by InitializeAVDAWithOverlay()
-  MockAndroidOverlay::Callbacks overlay_callbacks_;
-
-  // This must be a unique pointer to a VDA, not an AVDA, to ensure the
-  // the default_delete specialization that calls Destroy() will be used.
-  std::unique_ptr<VideoDecodeAccelerator> vda_;
-
-  AndroidVideoDecodeAccelerator* avda() {
-    return reinterpret_cast<AndroidVideoDecodeAccelerator*>(vda_.get());
-  }
-};
-
-TEST_P(AndroidVideoDecodeAcceleratorTest, ConfigureUnsupportedCodec) {
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  config_ = VideoDecodeAccelerator::Config(VIDEO_CODEC_PROFILE_UNKNOWN);
-  ASSERT_FALSE(InitializeAVDA());
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       ConfigureSupportedCodecSynchronously) {
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  config_.is_deferred_initialization_allowed = false;
-
-  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecSync(_, _));
-  // AVDA must set client callbacks even in sync mode, so that the chooser is
-  // in a sane state.  https://crbug.com/772899 .
-  EXPECT_CALL(*chooser_, MockSetClientCallbacks());
-  ASSERT_TRUE(InitializeAVDA());
-  testing::Mock::VerifyAndClearExpectations(chooser_);
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest, FailingToCreateACodecSyncIsAnError) {
-  // Failuew to create a codec during sync init should cause Initialize to fail.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  config_.is_deferred_initialization_allowed = false;
-  codec_allocator_->allow_sync_creation = false;
-
-  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecSync(nullptr, NotNull()));
-  ASSERT_FALSE(InitializeAVDA());
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest, FailingToCreateACodecAsyncIsAnError) {
-  // Verify that a null codec signals error for async init when it doesn't get a
-  // mediacodec instance.
-  //
-  // Also assert that there's only one call to CreateMediaCodecAsync. And since
-  // it replies with a null codec, AVDA will be in an error state when it shuts
-  // down.  Since we know that it's constructed before we destroy the VDA, we
-  // verify that AVDA doens't create codecs during destruction.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  // Note that if we somehow end up deferring surface creation, then this would
-  // no longer be expected to fail.  It would signal success before asking for a
-  // surface or codec.
-  EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, NotNull()));
-  EXPECT_CALL(client_, NotifyInitializationComplete(false));
-
-  ASSERT_TRUE(InitializeAVDA());
-  chooser_->ProvideTextureOwner();
-  codec_allocator_->ProvideNullCodecAsync();
-
-  // Make sure that codec allocation has happened before destroying the VDA.
-  testing::Mock::VerifyAndClearExpectations(codec_allocator_.get());
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       LowEndDevicesSucceedInitWithoutASurface) {
-  // If AVDA decides that we should defer surface creation, then it should
-  // signal success before we provide a surface.  It should still ask for a
-  // surface, though.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  EXPECT_CALL(*chooser_, MockUpdateState()).Times(0);
-  EXPECT_CALL(client_, NotifyInitializationComplete(true));
-
-  // It would be nicer if we didn't just force this on, since we might do so
-  // in a state that AVDA isn't supposed to handle (e.g., if we give it a
-  // surface, then it would never decide to defer surface creation).
-  bool force_defer_surface_creation = true;
-  InitializeAVDA(force_defer_surface_creation);
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest, AsyncInitWithTextureOwnerAndDelete) {
-  // When configuring with a TextureOwner and deferred init, we should be
-  // asked for a codec, and be notified of init success if we provide one. When
-  // AVDA is destroyed, it should release the codec and texture owner.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  InitializeAVDAWithTextureOwner();
-
-  // Delete the VDA, and make sure that it tries to free the codec and the right
-  // texture owner.
-  EXPECT_CALL(
-      *codec_allocator_,
-      MockReleaseMediaCodec(codec_allocator_->most_recent_codec,
-                            codec_allocator_->most_recent_overlay,
-                            codec_allocator_->most_recent_texture_owner));
-  codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction();
-  vda_ = nullptr;
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest, AsyncInitWithSurfaceAndDelete) {
-  // When |config_| specifies a surface, we should be given a factory during
-  // startup for it.  When |chooser_| provides an overlay, the codec should be
-  // allocated using it.  Shutdown should provide the overlay when releasing the
-  // media codec.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  InitializeAVDAWithOverlay();
-
-  // Delete the VDA, and make sure that it tries to free the codec and the
-  // overlay that it provided to us.
-  EXPECT_CALL(
-      *codec_allocator_,
-      MockReleaseMediaCodec(codec_allocator_->most_recent_codec,
-                            codec_allocator_->most_recent_overlay,
-                            codec_allocator_->most_recent_texture_owner));
-  codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction();
-  vda_ = nullptr;
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       SwitchesToTextureOwnerWhenSurfaceDestroyed) {
-  // Provide a surface, and a codec, then destroy the surface.  AVDA should use
-  // SetSurface to switch to TextureOwner.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  InitializeAVDAWithOverlay();
-
-  // It would be nice if we knew that this was a texture owner.  As it is, we
-  // just destroy the VDA and expect that we're provided with one.  Hopefully,
-  // AVDA is actually calling SetSurface properly.
-  EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_))
-      .WillOnce(Return(true));
-  codec_allocator_->most_recent_codec_destruction_observer
-      ->VerifyAndClearExpectations();
-  overlay_callbacks_.SurfaceDestroyed.Run();
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_CALL(*codec_allocator_,
-              MockReleaseMediaCodec(codec_allocator_->most_recent_codec,
-                                    nullptr, NotNull()));
-  vda_ = nullptr;
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest, SwitchesToTextureOwnerEventually) {
-  // Provide a surface, and a codec, then request that AVDA switches to a
-  // texture owner.  Verify that it does.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  InitializeAVDAWithOverlay();
-
-  EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_))
-      .WillOnce(Return(true));
-
-  // Note that it's okay if |avda_| switches before ProvideTextureOwner
-  // returns, since it has no queued output anyway.
-  chooser_->ProvideTextureOwner();
-  LetAVDAUpdateSurface();
-
-  // Verify that we're now using some texture owner.
-  EXPECT_CALL(*codec_allocator_,
-              MockReleaseMediaCodec(codec_allocator_->most_recent_codec,
-                                    nullptr, NotNull()));
-  codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction();
-  vda_ = nullptr;
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       SetSurfaceFailureDoesntSwitchSurfaces) {
-  // Initialize AVDA with a surface, then request that AVDA switches to a
-  // texture owner.  When it tries to UpdateSurface, pretend to fail.  AVDA
-  // should notify error, and also release the original surface.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  InitializeAVDAWithOverlay();
-
-  EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_))
-      .WillOnce(Return(false));
-  EXPECT_CALL(client_,
-              NotifyError(AndroidVideoDecodeAccelerator::PLATFORM_FAILURE))
-      .Times(1);
-  codec_allocator_->most_recent_codec_destruction_observer
-      ->VerifyAndClearExpectations();
-  chooser_->ProvideTextureOwner();
-  LetAVDAUpdateSurface();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       SwitchToSurfaceAndBackBeforeSetSurface) {
-  // Ask AVDA to switch from ST to overlay, then back to ST before it has a
-  // chance to do the first switch.  It should simply drop the overlay.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  InitializeAVDAWithTextureOwner();
-
-  // Don't let AVDA switch immediately, else it could choose to SetSurface when
-  // it first gets the overlay.
-  SetHasUnrenderedPictureBuffers(true);
-  EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0);
-  std::unique_ptr<MockAndroidOverlay> overlay =
-      std::make_unique<MockAndroidOverlay>();
-  // Make sure that the overlay is not destroyed too soon.
-  std::unique_ptr<DestructionObserver> observer =
-      overlay->CreateDestructionObserver();
-  observer->DoNotAllowDestruction();
-
-  chooser_->ProvideOverlay(std::move(overlay));
-
-  // Now it is expected to drop the overlay.
-  observer->ExpectDestruction();
-
-  // While the incoming surface is pending, switch back to TextureOwner.
-  chooser_->ProvideTextureOwner();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       ChangingOutputSurfaceVoluntarilyWithoutSetSurfaceIsIgnored) {
-  // If we ask AVDA to change to TextureOwner should be ignored on platforms
-  // that don't support SetSurface (pre-M or blacklisted).  It should also
-  // ignore TextureOwner => overlay, but we don't check that.
-  //
-  // Also note that there are other probably reasonable things to do (like
-  // signal an error), but we want to be sure that it doesn't try to SetSurface.
-  // We also want to be sure that, if it doesn't signal an error, that it also
-  // doesn't get confused about which surface is in use.  So, we assume that it
-  // doesn't signal an error, and we check that it releases the right surface
-  // with the codec.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-  EXPECT_CALL(client_, NotifyError(_)).Times(0);
-
-  ON_CALL(*device_info_, IsSetOutputSurfaceSupported())
-      .WillByDefault(Return(false));
-  InitializeAVDAWithOverlay();
-  EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0);
-
-  // This should not switch to TextureOwner.
-  chooser_->ProvideTextureOwner();
-  LetAVDAUpdateSurface();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       OnSurfaceDestroyedWithoutSetSurfaceFreesTheCodec) {
-  // If AVDA receives OnSurfaceDestroyed without support for SetSurface, then it
-  // should free the codec.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-  ON_CALL(*device_info_, IsSetOutputSurfaceSupported())
-      .WillByDefault(Return(false));
-  InitializeAVDAWithOverlay();
-  EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0);
-
-  // This should free the codec.
-  EXPECT_CALL(
-      *codec_allocator_,
-      MockReleaseMediaCodec(codec_allocator_->most_recent_codec,
-                            codec_allocator_->most_recent_overlay, nullptr));
-  codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction();
-  overlay_callbacks_.SurfaceDestroyed.Run();
-  base::RunLoop().RunUntilIdle();
-
-  // Verify that the codec has been released, since |vda_| will be destroyed
-  // soon.  The expectations must be met before that.
-  testing::Mock::VerifyAndClearExpectations(&codec_allocator_);
-  codec_allocator_->most_recent_codec_destruction_observer
-      ->VerifyAndClearExpectations();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       MultipleTextureOwnerCallbacksAreIgnored) {
-  // Ask AVDA to switch to ST when it's already using ST, nothing should happen.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-
-  InitializeAVDAWithTextureOwner();
-
-  // This should do nothing.
-  EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0);
-  chooser_->ProvideTextureOwner();
-
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       OverlayInfoWithDuplicateSurfaceIDDoesntChangeTheFactory) {
-  // Send OverlayInfo with duplicate info, and verify that it doesn't change
-  // the factory.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-  InitializeAVDAWithOverlay();
-
-  EXPECT_CALL(*chooser_, MockUpdateState()).Times(1);
-  EXPECT_CALL(*chooser_, MockReplaceOverlayFactory(_)).Times(0);
-  OverlayInfo overlay_info = config_.overlay_info;
-  avda()->SetOverlayInfo(overlay_info);
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest,
-       OverlayInfoWithNewSurfaceIDDoesChangeTheFactory) {
-  // Send OverlayInfo with new surface info, and verify that it does change the
-  // overlay factory.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-  InitializeAVDAWithOverlay();
-
-  EXPECT_CALL(*chooser_, MockUpdateState()).Times(1);
-  OverlayInfo overlay_info = config_.overlay_info;
-  overlay_info.routing_token = base::UnguessableToken::Create();
-  avda()->SetOverlayInfo(overlay_info);
-}
-
-TEST_P(AndroidVideoDecodeAcceleratorTest, FullscreenSignalIsSentToChooser) {
-  // Send OverlayInfo that has |is_fullscreen| set, and verify that the chooser
-  // is notified about it.
-  SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
-  InitializeAVDAWithOverlay();
-  OverlayInfo overlay_info = config_.overlay_info;
-  overlay_info.is_fullscreen = !config_.overlay_info.is_fullscreen;
-  avda()->SetOverlayInfo(overlay_info);
-  ASSERT_EQ(chooser_->current_state_.is_fullscreen, overlay_info.is_fullscreen);
-}
-
-static std::vector<VideoCodecProfile> GetTestList() {
-  std::vector<VideoCodecProfile> test_profiles;
-
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-  if (MediaCodecUtil::IsMediaCodecAvailable())
-    test_profiles.push_back(H264PROFILE_BASELINE);
-#endif
-
-  if (MediaCodecUtil::IsVp8DecoderAvailable())
-    test_profiles.push_back(VP8PROFILE_ANY);
-  if (MediaCodecUtil::IsVp9DecoderAvailable())
-    test_profiles.push_back(VP9PROFILE_PROFILE0);
-  return test_profiles;
-}
-
-INSTANTIATE_TEST_SUITE_P(AndroidVideoDecodeAcceleratorTest,
-                         AndroidVideoDecodeAcceleratorTest,
-                         testing::ValuesIn(GetTestList()));
-
-}  // namespace media
diff --git a/media/gpu/android/avda_codec_image.cc b/media/gpu/android/avda_codec_image.cc
deleted file mode 100644
index fd181d2..0000000
--- a/media/gpu/android/avda_codec_image.cc
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/gpu/android/avda_codec_image.h"
-
-#include <string.h>
-
-#include <memory>
-
-#include "gpu/command_buffer/service/texture_manager.h"
-#include "media/base/android/media_codec_bridge_impl.h"
-#include "media/gpu/android/avda_shared_state.h"
-#include "ui/gl/android/surface_texture.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/scoped_make_current.h"
-
-namespace media {
-
-AVDACodecImage::AVDACodecImage(
-    const scoped_refptr<AVDASharedState>& shared_state,
-    MediaCodecBridge* codec)
-    : shared_state_(shared_state),
-      codec_buffer_index_(kInvalidCodecBufferIndex),
-      media_codec_(codec),
-      has_texture_owner_(false),
-      texture_(0) {}
-
-AVDACodecImage::~AVDACodecImage() {}
-
-gfx::Size AVDACodecImage::GetSize() {
-  return size_;
-}
-
-unsigned AVDACodecImage::GetInternalFormat() {
-  return GL_RGBA;
-}
-
-AVDACodecImage::BindOrCopy AVDACodecImage::ShouldBindOrCopy() {
-  return COPY;
-}
-
-bool AVDACodecImage::BindTexImage(unsigned target) {
-  NOTREACHED();
-  return false;
-}
-
-void AVDACodecImage::ReleaseTexImage(unsigned target) {}
-
-bool AVDACodecImage::CopyTexImage(unsigned target) {
-  if (!has_texture_owner_ || target != GL_TEXTURE_EXTERNAL_OES)
-    return false;
-
-  GLint bound_service_id = 0;
-  glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
-  // We insist that the currently bound texture is the right one.
-  if (bound_service_id !=
-      static_cast<GLint>(shared_state_->texture_owner_service_id())) {
-    return false;
-  }
-
-  // Make sure that we have the right image in the front buffer.  Note that the
-  // bound_service_id is guaranteed to be equal to the texture owner's client
-  // texture id, so we can skip preserving it if the right context is current.
-  UpdateSurfaceInternal(UpdateMode::RENDER_TO_FRONT_BUFFER,
-                        kDontRestoreBindings);
-
-  // By setting image state to UNBOUND instead of COPIED we ensure that
-  // CopyTexImage() is called each time the texture owner is used for drawing.
-  // It would be nice if we could do this via asking for the currently bound
-  // Texture, but the active unit never seems to change.
-  texture_->SetLevelImageState(GL_TEXTURE_EXTERNAL_OES, 0,
-                               gpu::gles2::Texture::UNBOUND);
-
-  return true;
-}
-
-bool AVDACodecImage::CopyTexSubImage(unsigned target,
-                                     const gfx::Point& offset,
-                                     const gfx::Rect& rect) {
-  return false;
-}
-
-bool AVDACodecImage::ScheduleOverlayPlane(
-    gfx::AcceleratedWidget widget,
-    int z_order,
-    gfx::OverlayTransform transform,
-    const gfx::Rect& bounds_rect,
-    const gfx::RectF& crop_rect,
-    bool enable_blend,
-    std::unique_ptr<gfx::GpuFence> gpu_fence) {
-  // This should only be called when we're rendering to a SurfaceView.
-  if (has_texture_owner_) {
-    DVLOG(1) << "Invalid call to ScheduleOverlayPlane; this image is "
-                "TextureOwner backed.";
-    return false;
-  }
-
-  // Move the overlay if needed.
-  if (shared_state_->overlay() && most_recent_bounds_ != bounds_rect) {
-    most_recent_bounds_ = bounds_rect;
-    shared_state_->overlay()->ScheduleLayout(bounds_rect);
-  }
-
-  UpdateSurface(UpdateMode::RENDER_TO_FRONT_BUFFER);
-  return true;
-}
-
-void AVDACodecImage::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
-                                  uint64_t process_tracing_id,
-                                  const std::string& dump_name) {}
-
-void AVDACodecImage::UpdateTextureOwner(RestoreBindingsMode mode) {
-  DCHECK(has_texture_owner_);
-  DCHECK_EQ(codec_buffer_index_, kUpdateOnly);
-  codec_buffer_index_ = kRendered;
-
-  // Swap the rendered image to the front.
-  std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current =
-      MakeCurrentIfNeeded();
-
-  // If we changed contexts, then we always want to restore it, since the caller
-  // doesn't know that we're switching contexts.
-  if (scoped_make_current)
-    mode = kDoRestoreBindings;
-
-  // Save the current binding if requested.
-  GLint bound_service_id = 0;
-  if (mode == kDoRestoreBindings)
-    glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
-
-  shared_state_->UpdateTexImage();
-  if (mode == kDoRestoreBindings)
-    glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
-}
-
-void AVDACodecImage::UpdateSurface(UpdateMode update_mode) {
-  UpdateSurfaceInternal(update_mode, kDoRestoreBindings);
-}
-
-void AVDACodecImage::CodecChanged(MediaCodecBridge* codec) {
-  media_codec_ = codec;
-  codec_buffer_index_ = kInvalidCodecBufferIndex;
-}
-
-void AVDACodecImage::SetBufferMetadata(int buffer_index,
-                                       bool has_texture_owner,
-                                       const gfx::Size& size) {
-  has_texture_owner_ = has_texture_owner;
-  codec_buffer_index_ = buffer_index;
-  size_ = size;
-}
-
-bool AVDACodecImage::SetSharedState(
-    scoped_refptr<AVDASharedState> shared_state) {
-  if (shared_state == shared_state_)
-    return false;
-  shared_state_ = shared_state;
-  most_recent_bounds_ = gfx::Rect();
-  return true;
-}
-
-void AVDACodecImage::UpdateSurfaceInternal(
-    UpdateMode update_mode,
-    RestoreBindingsMode attached_bindings_mode) {
-  if (!IsCodecBufferOutstanding())
-    return;
-
-  ReleaseOutputBuffer(update_mode);
-
-  // SurfaceViews are updated implicitly, so no further steps are necessary.
-  if (!has_texture_owner_) {
-    DCHECK(update_mode != UpdateMode::RENDER_TO_BACK_BUFFER);
-    return;
-  }
-
-  // If front buffer rendering hasn't been requested, exit early.
-  if (update_mode != UpdateMode::RENDER_TO_FRONT_BUFFER)
-    return;
-
-  UpdateTextureOwner(attached_bindings_mode);
-}
-
-void AVDACodecImage::ReleaseOutputBuffer(UpdateMode update_mode) {
-  DCHECK(IsCodecBufferOutstanding());
-
-  // In case of discard, simply discard and clear our codec buffer index.
-  if (update_mode == UpdateMode::DISCARD_CODEC_BUFFER) {
-    if (codec_buffer_index_ != kUpdateOnly)
-      media_codec_->ReleaseOutputBuffer(codec_buffer_index_, false);
-
-    // Note: No need to wait for the frame to be available in the kUpdateOnly
-    // case since it will be or has been waited on by another release call.
-    codec_buffer_index_ = kInvalidCodecBufferIndex;
-    return;
-  }
-
-  DCHECK(update_mode == UpdateMode::RENDER_TO_BACK_BUFFER ||
-         update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER);
-
-  if (!has_texture_owner_) {
-    DCHECK(update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER);
-    DCHECK_GE(codec_buffer_index_, 0);
-    media_codec_->ReleaseOutputBuffer(codec_buffer_index_, true);
-    codec_buffer_index_ = kRendered;
-    return;
-  }
-
-  // If we've already released to the back buffer, there's nothing left to do,
-  // but wait for the previously released buffer if necessary.
-  if (codec_buffer_index_ != kUpdateOnly) {
-    DCHECK(has_texture_owner_);
-    DCHECK_GE(codec_buffer_index_, 0);
-    shared_state_->RenderCodecBufferToTextureOwner(media_codec_,
-                                                   codec_buffer_index_);
-    codec_buffer_index_ = kUpdateOnly;
-  }
-
-  // Only wait for the TextureOwner update if we're rendering to the front.
-  if (update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER)
-    shared_state_->WaitForFrameAvailable();
-}
-
-std::unique_ptr<ui::ScopedMakeCurrent> AVDACodecImage::MakeCurrentIfNeeded() {
-  DCHECK(shared_state_->context());
-  // Remember: virtual contexts return true if and only if their shared context
-  // is current, regardless of which virtual context it is.
-  return std::unique_ptr<ui::ScopedMakeCurrent>(
-      shared_state_->context()->IsCurrent(nullptr)
-          ? nullptr
-          : new ui::ScopedMakeCurrent(shared_state_->context(),
-                                      shared_state_->surface()));
-}
-
-void AVDACodecImage::GetTextureMatrix(float matrix[16]) {
-  // Our current matrix may be stale.  Update it if possible.
-  if (has_texture_owner_)
-    UpdateSurface(UpdateMode::RENDER_TO_FRONT_BUFFER);
-  shared_state_->GetTransformMatrix(matrix);
-  YInvertMatrix(matrix);
-}
-
-void AVDACodecImage::NotifyPromotionHint(bool promotion_hint,
-                                         int display_x,
-                                         int display_y,
-                                         int display_width,
-                                         int display_height) {
-  shared_state_->GetPromotionHintCB().Run(PromotionHintAggregator::Hint(
-      gfx::Rect(display_x, display_y, display_width, display_height),
-      promotion_hint));
-}
-
-bool AVDACodecImage::IsCodecBufferOutstanding() const {
-  static_assert(kUpdateOnly < 0 && kUpdateOnly > kRendered &&
-                    kRendered > kInvalidCodecBufferIndex,
-                "Codec buffer index enum values are not ordered correctly.");
-  return codec_buffer_index_ > kRendered && media_codec_;
-}
-
-}  // namespace media
diff --git a/media/gpu/android/avda_codec_image.h b/media/gpu/android/avda_codec_image.h
deleted file mode 100644
index c3011e7c..0000000
--- a/media/gpu/android/avda_codec_image.h
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_ANDROID_AVDA_CODEC_IMAGE_H_
-#define MEDIA_GPU_ANDROID_AVDA_CODEC_IMAGE_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/macros.h"
-#include "gpu/command_buffer/service/gl_stream_texture_image.h"
-#include "media/gpu/android/avda_shared_state.h"
-
-namespace ui {
-class ScopedMakeCurrent;
-}
-
-namespace media {
-
-class MediaCodecBridge;
-
-// GLImage that renders MediaCodec buffers to a TextureOwner or SurfaceView as
-// needed in order to draw them.
-class AVDACodecImage : public gpu::gles2::GLStreamTextureImage {
- public:
-  AVDACodecImage(const scoped_refptr<AVDASharedState>& shared_state,
-                 MediaCodecBridge* codec);
-
-  // gl::GLImage implementation
-  gfx::Size GetSize() override;
-  unsigned GetInternalFormat() override;
-  BindOrCopy ShouldBindOrCopy() override;
-  bool BindTexImage(unsigned target) override;
-  void ReleaseTexImage(unsigned target) override;
-  bool CopyTexImage(unsigned target) override;
-  bool CopyTexSubImage(unsigned target,
-                       const gfx::Point& offset,
-                       const gfx::Rect& rect) override;
-  bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
-                            int z_order,
-                            gfx::OverlayTransform transform,
-                            const gfx::Rect& bounds_rect,
-                            const gfx::RectF& crop_rect,
-                            bool enable_blend,
-                            std::unique_ptr<gfx::GpuFence> gpu_fence) override;
-  void SetColorSpace(const gfx::ColorSpace& color_space) override {}
-  void Flush() override {}
-  void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
-                    uint64_t process_tracing_id,
-                    const std::string& dump_name) override;
-  // gpu::gles2::GLStreamTextureMatrix implementation
-  void GetTextureMatrix(float xform[16]) override;
-  void NotifyPromotionHint(bool promotion_hint,
-                           int display_x,
-                           int display_y,
-                           int display_width,
-                           int display_height) override;
-
-  enum class UpdateMode {
-    // Discards the codec buffer, no UpdateTexImage().
-    DISCARD_CODEC_BUFFER,
-
-    // Renders to back buffer, no UpdateTexImage(); can only be used with a
-    // valid |texture_owner_|.
-    RENDER_TO_BACK_BUFFER,
-
-    // Renders to the back buffer. When used with a SurfaceView, promotion to
-    // the front buffer is automatic. When using a |texture_owner_|,
-    // UpdateTexImage() is called to promote the back buffer into the front.
-    RENDER_TO_FRONT_BUFFER
-  };
-
-  // Releases the attached codec buffer (if not already released) indicated by
-  // |codec_buffer_index_| and updates the surface if specified by the given
-  // |update_mode|.  See UpdateMode documentation for details.
-  void UpdateSurface(UpdateMode update_mode);
-
-  // Updates the MediaCodec for this image; clears |codec_buffer_index_|.
-  void CodecChanged(MediaCodecBridge* codec);
-
-  void set_texture(gpu::gles2::Texture* texture) { texture_ = texture; }
-
-  // Sets up the properties necessary for the image to render. |buffer_index| is
-  // supplied to ReleaseOutputBuffer(), |has_texture_owner| controls which
-  // rendering path is used, and |size| is used by the compositor.
-  void SetBufferMetadata(int buffer_index,
-                         bool has_texture_owner,
-                         const gfx::Size& size);
-
-  bool SetSharedState(scoped_refptr<AVDASharedState> shared_state);
-
-  // Indicates if the codec buffer has been released to the back buffer.
-  bool was_rendered_to_back_buffer() const {
-    return codec_buffer_index_ == kUpdateOnly;
-  }
-
-  // Indicates if the codec buffer has been released to the front buffer.
-  bool was_rendered_to_front_buffer() const {
-    return codec_buffer_index_ == kRendered;
-  }
-
-  bool is_unrendered() const { return codec_buffer_index_ >= kUpdateOnly; }
-
- protected:
-  ~AVDACodecImage() override;
-
- private:
-  // Make sure that the texture owner's front buffer is current.  This will
-  // save / restore the current context.  It will optionally restore the texture
-  // bindings in the texture owner's context, based on |mode|.  This is
-  // intended as a hint if we don't need to change contexts.  If we do need to
-  // change contexts, then we'll always preserve the texture bindings in the
-  // both contexts.  In other words, the caller is telling us whether it's
-  // okay to change the binding in the current context.
-  enum RestoreBindingsMode { kDontRestoreBindings, kDoRestoreBindings };
-  void UpdateTextureOwner(RestoreBindingsMode mode);
-
-  // Internal helper for UpdateSurface() that allows callers to specify the
-  // RestoreBindingsMode when a TextureOwner is already attached prior to
-  // calling this method.
-  void UpdateSurfaceInternal(UpdateMode update_mode,
-                             RestoreBindingsMode attached_bindings_mode);
-
-  // Releases the attached codec buffer (if not already released) indicated by
-  // |codec_buffer_index_|. Never updates the actual surface. See UpdateMode
-  // documentation for details. For the purposes of this function the values
-  // RENDER_TO_FRONT_BUFFER and RENDER_TO_BACK_BUFFER do the same thing.
-  void ReleaseOutputBuffer(UpdateMode update_mode);
-
-  // Make shared_state_->context() current if it isn't already.
-  std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded();
-
-  // Return whether there is a codec buffer that we haven't rendered yet.  Will
-  // return false also if there's no codec or we otherwise can't update.
-  bool IsCodecBufferOutstanding() const;
-
-  // Shared state between the AVDA and all AVDACodecImages.
-  scoped_refptr<AVDASharedState> shared_state_;
-
-  // The MediaCodec buffer index that we should render. Must be >= 0 or one of
-  // the enum values below.
-  enum { kUpdateOnly = -1, kRendered = -2, kInvalidCodecBufferIndex = -3 };
-  int codec_buffer_index_;
-
-  // Our image size.
-  gfx::Size size_;
-
-  // May be null.
-  MediaCodecBridge* media_codec_;
-
-  // Indicates if we're rendering to a TextureOwner or not. Set during the
-  // call to SetBufferMetadata().
-  bool has_texture_owner_;
-
-  // The texture that we're attached to.
-  gpu::gles2::Texture* texture_;
-
-  // Bounds that we last sent to our overlay.
-  gfx::Rect most_recent_bounds_;
-
-  DISALLOW_COPY_AND_ASSIGN(AVDACodecImage);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_ANDROID_AVDA_CODEC_IMAGE_H_
diff --git a/media/gpu/android/avda_picture_buffer_manager.cc b/media/gpu/android/avda_picture_buffer_manager.cc
deleted file mode 100644
index 80ff9c4a..0000000
--- a/media/gpu/android/avda_picture_buffer_manager.cc
+++ /dev/null
@@ -1,273 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/gpu/android/avda_picture_buffer_manager.h"
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include "base/android/build_info.h"
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-#include "base/stl_util.h"
-#include "gpu/command_buffer/service/context_group.h"
-#include "gpu/command_buffer/service/gl_stream_texture_image.h"
-#include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
-#include "gpu/command_buffer/service/texture_manager.h"
-#include "gpu/ipc/service/gpu_channel.h"
-#include "media/base/android/media_codec_bridge_impl.h"
-#include "media/gpu/android/avda_codec_image.h"
-#include "media/gpu/android/avda_shared_state.h"
-#include "ui/gl/android/scoped_java_surface.h"
-#include "ui/gl/android/surface_texture.h"
-#include "ui/gl/egl_util.h"
-#include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_surface_egl.h"
-#include "ui/gl/scoped_binders.h"
-#include "ui/gl/scoped_make_current.h"
-
-// If !|ptr|, log a message, notify |state_provider_| of the error, and
-// return an optional value.
-#define RETURN_IF_NULL(ptr, ...)                                           \
-  do {                                                                     \
-    if (!(ptr)) {                                                          \
-      DLOG(ERROR) << "Got null for " << #ptr;                              \
-      state_provider_->NotifyError(VideoDecodeAccelerator::ILLEGAL_STATE); \
-      return __VA_ARGS__;                                                  \
-    }                                                                      \
-  } while (0)
-
-namespace media {
-
-AVDAPictureBufferManager::AVDAPictureBufferManager(
-    AVDAStateProvider* state_provider)
-    : state_provider_(state_provider), media_codec_(nullptr) {}
-
-AVDAPictureBufferManager::~AVDAPictureBufferManager() {}
-
-bool AVDAPictureBufferManager::Initialize(
-    scoped_refptr<AVDASurfaceBundle> surface_bundle) {
-  shared_state_ = nullptr;
-  texture_owner_ = nullptr;
-
-  if (!surface_bundle->overlay) {
-    // Create the texture owner.
-    // TODO(liberato): Don't memorize this.  However, since this entire path is
-    // deprecated, it's probably okay.
-    std::unique_ptr<gpu::gles2::AbstractTexture> texture =
-        state_provider_->CreateAbstractTexture(GL_TEXTURE_EXTERNAL_OES, GL_RGBA,
-                                               0,  // width,
-                                               0,  // height
-                                               1,  // depth
-                                               0,  // border
-                                               GL_RGBA, GL_UNSIGNED_BYTE);
-    texture_owner_ = TextureOwner::Create(
-        std::move(texture), TextureOwner::Mode::kSurfaceTextureInsecure);
-    if (!texture_owner_)
-      return false;
-
-    surface_bundle->texture_owner_surface = texture_owner_->CreateJavaSurface();
-    surface_bundle->texture_owner_ = texture_owner_;
-  }
-
-  // Only do this once the texture owner is filled in, since the constructor
-  // assumes that it will be.
-  shared_state_ = new AVDASharedState(surface_bundle);
-  shared_state_->SetPromotionHintCB(state_provider_->GetPromotionHintCB());
-
-  return true;
-}
-
-void AVDAPictureBufferManager::Destroy(const PictureBufferMap& buffers) {
-  // Do nothing if Initialize() has not been called.
-  if (!shared_state_)
-    return;
-
-  ReleaseCodecBuffers(buffers);
-  CodecChanged(nullptr);
-  texture_owner_ = nullptr;
-}
-
-void AVDAPictureBufferManager::SetImageForPicture(
-    const PictureBuffer& picture_buffer,
-    gpu::gles2::GLStreamTextureImage* image) {
-  auto* context_group = state_provider_->GetContextGroup();
-  RETURN_IF_NULL(context_group);
-  auto* texture_manager = context_group->texture_manager();
-  RETURN_IF_NULL(texture_manager);
-
-  DCHECK_LE(1u, picture_buffer.client_texture_ids().size());
-  gpu::gles2::TextureRef* texture_ref =
-      texture_manager->GetTexture(picture_buffer.client_texture_ids()[0]);
-  RETURN_IF_NULL(texture_ref);
-
-  // Default to zero which will clear the stream texture service id if one was
-  // previously set.
-  GLuint stream_texture_service_id = 0;
-  if (image) {
-    // Override the Texture's service id, so that it will use the one that is
-    // attached to the TextureOwner
-    stream_texture_service_id = shared_state_->texture_owner_service_id();
-
-    // Also set the parameters for the level if we're not clearing the image.
-    const gfx::Size size = state_provider_->GetSize();
-    texture_manager->SetLevelInfo(texture_ref, kTextureTarget, 0, GL_RGBA,
-                                  size.width(), size.height(), 1, 0, GL_RGBA,
-                                  GL_UNSIGNED_BYTE, gfx::Rect());
-
-    static_cast<AVDACodecImage*>(image)->set_texture(texture_ref->texture());
-  }
-
-  // If we're clearing the image, or setting a TextureOwner backed image, we
-  // set the state to UNBOUND. For TextureOwner images, this ensures that the
-  // implementation will call CopyTexImage, which is where AVDACodecImage
-  // updates the TextureOwner to the right frame.
-  auto image_state = gpu::gles2::Texture::UNBOUND;
-  // For SurfaceView we set the state to BOUND because ScheduleOverlayPlane
-  // requires it. If something tries to sample from this texture it won't work,
-  // but there's no way to sample from a SurfaceView anyway, so it doesn't
-  // matter.
-  if (image && !texture_owner_)
-    image_state = gpu::gles2::Texture::BOUND;
-  texture_manager->SetLevelStreamTextureImage(texture_ref, kTextureTarget, 0,
-                                              image, image_state,
-                                              stream_texture_service_id);
-  texture_manager->SetLevelCleared(texture_ref, kTextureTarget, 0, true);
-}
-
-AVDACodecImage* AVDAPictureBufferManager::GetImageForPicture(
-    int picture_buffer_id) const {
-  auto it = codec_images_.find(picture_buffer_id);
-  DCHECK(it != codec_images_.end());
-  return it->second.get();
-}
-
-void AVDAPictureBufferManager::UseCodecBufferForPictureBuffer(
-    int32_t codec_buf_index,
-    const PictureBuffer& picture_buffer) {
-  // Notify the AVDACodecImage for picture_buffer that it should use the
-  // decoded buffer codec_buf_index to render this frame.
-  AVDACodecImage* avda_image = GetImageForPicture(picture_buffer.id());
-
-  // Note that this is not a race, since we do not re-use a PictureBuffer
-  // until after the CC is done drawing it.
-  pictures_out_for_display_.push_back(picture_buffer.id());
-  avda_image->SetBufferMetadata(codec_buf_index, !!texture_owner_,
-                                state_provider_->GetSize());
-
-  // If the shared state has changed for this image, retarget its texture.
-  if (avda_image->SetSharedState(shared_state_))
-    SetImageForPicture(picture_buffer, avda_image);
-
-  MaybeRenderEarly();
-}
-
-void AVDAPictureBufferManager::AssignOnePictureBuffer(
-    const PictureBuffer& picture_buffer,
-    bool have_context) {
-  // Attach a GLImage to each texture that will use the texture owner.
-  scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image =
-      codec_images_[picture_buffer.id()] =
-          new AVDACodecImage(shared_state_, media_codec_);
-  SetImageForPicture(picture_buffer, gl_image.get());
-}
-
-void AVDAPictureBufferManager::ReleaseCodecBufferForPicture(
-    const PictureBuffer& picture_buffer) {
-  GetImageForPicture(picture_buffer.id())
-      ->UpdateSurface(AVDACodecImage::UpdateMode::DISCARD_CODEC_BUFFER);
-}
-
-void AVDAPictureBufferManager::ReuseOnePictureBuffer(
-    const PictureBuffer& picture_buffer) {
-  base::Erase(pictures_out_for_display_, picture_buffer.id());
-
-  // At this point, the CC must be done with the picture.  We can't really
-  // check for that here directly.  it's guaranteed in gpu_video_decoder.cc,
-  // when it waits on the sync point before releasing the mailbox.  That sync
-  // point is inserted by destroying the resource in VideoLayerImpl::DidDraw.
-  ReleaseCodecBufferForPicture(picture_buffer);
-  MaybeRenderEarly();
-}
-
-void AVDAPictureBufferManager::ReleaseCodecBuffers(
-    const PictureBufferMap& buffers) {
-  for (const std::pair<int, PictureBuffer>& entry : buffers)
-    ReleaseCodecBufferForPicture(entry.second);
-}
-
-void AVDAPictureBufferManager::MaybeRenderEarly() {
-  if (pictures_out_for_display_.empty())
-    return;
-
-  // See if we can consume the front buffer / render to the SurfaceView. Iterate
-  // in reverse to find the most recent front buffer. If none is found, the
-  // |front_index| will point to the beginning of the array.
-  size_t front_index = pictures_out_for_display_.size() - 1;
-  AVDACodecImage* first_renderable_image = nullptr;
-  for (int i = front_index; i >= 0; --i) {
-    const int id = pictures_out_for_display_[i];
-    AVDACodecImage* avda_image = GetImageForPicture(id);
-
-    // Update the front buffer index as we move along to shorten the number of
-    // candidate images we look at for back buffer rendering.
-    front_index = i;
-    first_renderable_image = avda_image;
-
-    // If we find a front buffer, stop and indicate that front buffer rendering
-    // is not possible since another image is already in the front buffer.
-    if (avda_image->was_rendered_to_front_buffer()) {
-      first_renderable_image = nullptr;
-      break;
-    }
-  }
-
-  if (first_renderable_image) {
-    first_renderable_image->UpdateSurface(
-        AVDACodecImage::UpdateMode::RENDER_TO_FRONT_BUFFER);
-  }
-
-  // Back buffer rendering is only available for texture owners. We'll always
-  // have at least one front buffer, so the next buffer must be the backbuffer.
-  size_t backbuffer_index = front_index + 1;
-  if (!texture_owner_ || backbuffer_index >= pictures_out_for_display_.size())
-    return;
-
-  // See if the back buffer is free. If so, then render the frame adjacent to
-  // the front buffer.  The listing is in render order, so we can just use the
-  // first unrendered frame if there is back buffer space.
-  first_renderable_image =
-      GetImageForPicture(pictures_out_for_display_[backbuffer_index]);
-  if (first_renderable_image->was_rendered_to_back_buffer())
-    return;
-
-  // Due to the loop in the beginning this should never be true.
-  DCHECK(!first_renderable_image->was_rendered_to_front_buffer());
-  first_renderable_image->UpdateSurface(
-      AVDACodecImage::UpdateMode::RENDER_TO_BACK_BUFFER);
-}
-
-void AVDAPictureBufferManager::CodecChanged(MediaCodecBridge* codec) {
-  media_codec_ = codec;
-  for (auto& image_kv : codec_images_)
-    image_kv.second->CodecChanged(codec);
-  shared_state_->ClearReleaseTime();
-}
-
-bool AVDAPictureBufferManager::ArePicturesOverlayable() {
-  // SurfaceView frames are always overlayable because that's the only way to
-  // display them.
-  return !texture_owner_;
-}
-
-bool AVDAPictureBufferManager::HasUnrenderedPictures() const {
-  for (int id : pictures_out_for_display_) {
-    if (GetImageForPicture(id)->is_unrendered())
-      return true;
-  }
-  return false;
-}
-
-}  // namespace media
diff --git a/media/gpu/android/avda_picture_buffer_manager.h b/media/gpu/android/avda_picture_buffer_manager.h
deleted file mode 100644
index b93e324..0000000
--- a/media/gpu/android/avda_picture_buffer_manager.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_ANDROID_AVDA_PICTURE_BUFFER_MANAGER_H_
-#define MEDIA_GPU_ANDROID_AVDA_PICTURE_BUFFER_MANAGER_H_
-
-#include <stdint.h>
-#include <vector>
-
-#include "base/macros.h"
-#include "media/gpu/android/avda_state_provider.h"
-#include "media/gpu/android/avda_surface_bundle.h"
-#include "media/gpu/android/surface_texture_gl_owner.h"
-#include "media/gpu/media_gpu_export.h"
-
-namespace gpu {
-namespace gles2 {
-class GLStreamTextureImage;
-}
-}  // namespace gpu
-
-namespace media {
-class AVDACodecImage;
-class AVDASharedState;
-class MediaCodecBridge;
-
-// AVDAPictureBufferManager is used by AVDA to associate its PictureBuffers with
-// MediaCodec output buffers. It attaches AVDACodecImages to the PictureBuffer
-// textures so that when they're used to draw the AVDACodecImage can release the
-// MediaCodec buffer to the backing Surface. If the Surface is a TextureOwner,
-// the front buffer can then be used to draw without needing to copy the pixels.
-// If the Surface is a SurfaceView, the release causes the frame to be displayed
-// immediately.
-class MEDIA_GPU_EXPORT AVDAPictureBufferManager {
- public:
-  using PictureBufferMap = std::map<int32_t, PictureBuffer>;
-
-  explicit AVDAPictureBufferManager(AVDAStateProvider* state_provider);
-  virtual ~AVDAPictureBufferManager();
-
-  // Call Initialize, providing the surface bundle that holds the surface that
-  // will back the frames.  If an overlay is present in the bundle, then this
-  // will set us up to render codec buffers at the appropriate time for display,
-  // but will assume that consuming the resulting buffers is handled elsewhere
-  // (e.g., SurfaceFlinger).  We will ensure that any reference to the bundle
-  // is dropped if the overlay sends OnSurfaceDestroyed.
-  //
-  // Without an overlay, we will create a TextureOwner and add it (and its
-  // surface) to |surface_bundle|.  We will arrange to consume the buffers at
-  // the right time, in addition to releasing the codec buffers for rendering.
-  //
-  // One may call these multiple times to change between overlay and ST.
-  //
-  // Picture buffers will be updated to reflect the new surface during the call
-  // to UseCodecBufferForPicture().
-  //
-  // Returns true on success.
-  bool Initialize(scoped_refptr<AVDASurfaceBundle> surface_bundle);
-
-  void Destroy(const PictureBufferMap& buffers);
-
-  // Sets up |picture_buffer| so that its texture will refer to the image that
-  // is represented by the decoded output buffer at codec_buffer_index.
-  void UseCodecBufferForPictureBuffer(int32_t codec_buffer_index,
-                                      const PictureBuffer& picture_buffer);
-
-  // Assigns a picture buffer and attaches an image to its texture.
-  void AssignOnePictureBuffer(const PictureBuffer& picture_buffer,
-                              bool have_context);
-
-  // Reuses a picture buffer to hold a new frame.
-  void ReuseOnePictureBuffer(const PictureBuffer& picture_buffer);
-
-  // Release MediaCodec buffers.
-  void ReleaseCodecBuffers(const PictureBufferMap& buffers);
-
-  // Attempts to free up codec output buffers by rendering early.
-  void MaybeRenderEarly();
-
-  // Called when the MediaCodec instance changes. If |codec| is nullptr the
-  // MediaCodec is being destroyed. Previously provided codecs should no longer
-  // be referenced.
-  void CodecChanged(MediaCodecBridge* codec);
-
-  // Whether the pictures buffers are overlayable.
-  bool ArePicturesOverlayable();
-
-  // Are there any unrendered picture buffers oustanding?
-  bool HasUnrenderedPictures() const;
-
-  // Returns the GL texture target that the PictureBuffer textures use.
-  // Always use OES textures even though this will cause flickering in dev tools
-  // when inspecting a fullscreen video.  See http://crbug.com/592798
-  static constexpr GLenum kTextureTarget = GL_TEXTURE_EXTERNAL_OES;
-
- private:
-  // Release any codec buffer that is associated with the given picture buffer
-  // back to the codec.  It is okay if there is no such buffer.
-  void ReleaseCodecBufferForPicture(const PictureBuffer& picture_buffer);
-
-  // Sets up the texture references (as found by |picture_buffer|), for the
-  // specified |image|. If |image| is null, clears any ref on the texture
-  // associated with |picture_buffer|.
-  void SetImageForPicture(const PictureBuffer& picture_buffer,
-                          gpu::gles2::GLStreamTextureImage* image);
-
-  AVDACodecImage* GetImageForPicture(int picture_buffer_id) const;
-
-  scoped_refptr<AVDASharedState> shared_state_;
-
-  AVDAStateProvider* const state_provider_;
-
-  // The texture owner to render to. Non-null after Initialize() if
-  // we're not rendering to a SurfaceView.
-  scoped_refptr<TextureOwner> texture_owner_;
-
-  MediaCodecBridge* media_codec_;
-
-  // Picture buffer IDs that are out for display. Stored in order of frames as
-  // they are returned from the decoder.
-  std::vector<int32_t> pictures_out_for_display_;
-
-  // Maps a picture buffer id to a AVDACodecImage.
-  std::map<int, scoped_refptr<AVDACodecImage>> codec_images_;
-
-  DISALLOW_COPY_AND_ASSIGN(AVDAPictureBufferManager);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_ANDROID_AVDA_PICTURE_BUFFER_MANAGER_H_
diff --git a/media/gpu/android/avda_shared_state.cc b/media/gpu/android/avda_shared_state.cc
deleted file mode 100644
index 2d5e1c1b..0000000
--- a/media/gpu/android/avda_shared_state.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/gpu/android/avda_shared_state.h"
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "media/gpu/android/avda_codec_image.h"
-#include "ui/gl/android/surface_texture.h"
-#include "ui/gl/gl_bindings.h"
-#include "ui/gl/scoped_make_current.h"
-
-namespace media {
-
-AVDASharedState::AVDASharedState(
-    scoped_refptr<AVDASurfaceBundle> surface_bundle)
-    : gl_matrix_{
-          1, 0, 0, 0,  // Default to a sane guess just in case we can't get the
-          0, 1, 0, 0,  // matrix on the first call. Will be Y-flipped later.
-          0, 0, 1, 0,  //
-          0, 0, 0, 1,  // Comment preserves 4x4 formatting.
-      },
-      surface_bundle_(surface_bundle),
-      weak_this_factory_(this) {
-  // If we're holding a reference to an overlay, then register to drop it if the
-  // overlay's surface is destroyed.
-  if (overlay()) {
-    overlay()->AddSurfaceDestroyedCallback(base::Bind(
-        &AVDASharedState::ClearOverlay, weak_this_factory_.GetWeakPtr()));
-  }
-}
-
-AVDASharedState::~AVDASharedState() = default;
-
-void AVDASharedState::RenderCodecBufferToTextureOwner(MediaCodecBridge* codec,
-                                                      int codec_buffer_index) {
-  if (texture_owner()->IsExpectingFrameAvailable())
-    texture_owner()->WaitForFrameAvailable();
-  codec->ReleaseOutputBuffer(codec_buffer_index, true);
-  texture_owner()->SetReleaseTimeToNow();
-}
-
-void AVDASharedState::WaitForFrameAvailable() {
-  texture_owner()->WaitForFrameAvailable();
-}
-
-void AVDASharedState::UpdateTexImage() {
-  texture_owner()->UpdateTexImage();
-  if (!texture_owner()->binds_texture_on_update())
-    texture_owner()->EnsureTexImageBound();
-  // Helpfully, this is already column major.
-  texture_owner()->GetTransformMatrix(gl_matrix_);
-}
-
-void AVDASharedState::GetTransformMatrix(float matrix[16]) const {
-  memcpy(matrix, gl_matrix_, sizeof(gl_matrix_));
-}
-
-void AVDASharedState::ClearReleaseTime() {
-  if (texture_owner())
-    texture_owner()->IgnorePendingRelease();
-}
-
-void AVDASharedState::ClearOverlay(AndroidOverlay* overlay_raw) {
-  if (surface_bundle_ && overlay() == overlay_raw)
-    surface_bundle_ = nullptr;
-}
-
-void AVDASharedState::SetPromotionHintCB(
-    PromotionHintAggregator::NotifyPromotionHintCB cb) {
-  promotion_hint_cb_ = cb;
-}
-
-const PromotionHintAggregator::NotifyPromotionHintCB&
-AVDASharedState::GetPromotionHintCB() {
-  return promotion_hint_cb_;
-}
-
-}  // namespace media
diff --git a/media/gpu/android/avda_shared_state.h b/media/gpu/android/avda_shared_state.h
deleted file mode 100644
index 954f775..0000000
--- a/media/gpu/android/avda_shared_state.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_ANDROID_AVDA_SHARED_STATE_H_
-#define MEDIA_GPU_ANDROID_AVDA_SHARED_STATE_H_
-
-#include "base/memory/weak_ptr.h"
-#include "base/synchronization/waitable_event.h"
-#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
-#include "media/base/android/android_overlay.h"
-#include "media/base/android/media_codec_bridge.h"
-#include "media/gpu/android/avda_shared_state.h"
-#include "media/gpu/android/avda_surface_bundle.h"
-#include "media/gpu/android/promotion_hint_aggregator.h"
-#include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_image.h"
-#include "ui/gl/gl_surface.h"
-
-namespace media {
-
-// State shared by AVDACodecImages.  This holds a reference to the surface
-// bundle that's backing the frames.  If it's an overlay, then we'll
-// automatically drop our reference to the bundle if the overlay's surface gets
-// an OnSurfaceDestroyed.
-// TODO(watk): This doesn't really do anything any more; we should delete it.
-class AVDASharedState : public base::RefCounted<AVDASharedState> {
- public:
-  AVDASharedState(scoped_refptr<AVDASurfaceBundle> surface_bundle);
-
-  GLuint texture_owner_service_id() const {
-    return texture_owner() ? texture_owner()->GetTextureId() : 0;
-  }
-
-  TextureOwner* texture_owner() const {
-    return surface_bundle_ ? surface_bundle_->texture_owner_.get() : nullptr;
-  }
-
-  AndroidOverlay* overlay() const {
-    return surface_bundle_ ? surface_bundle_->overlay.get() : nullptr;
-  }
-
-  // Context and surface that |texture_owner_| is bound to, if
-  // |texture_owner_| is not null.
-  gl::GLContext* context() const {
-    return texture_owner() ? texture_owner()->GetContext() : nullptr;
-  }
-
-  gl::GLSurface* surface() const {
-    return texture_owner() ? texture_owner()->GetSurface() : nullptr;
-  }
-
-  // Helper method for coordinating the interactions between
-  // MediaCodec::ReleaseOutputBuffer() and WaitForFrameAvailable() when
-  // rendering to a TextureOwner; this method should never be called when
-  // rendering to a SurfaceView.
-  //
-  // The release of the codec buffer to the texture owner is asynchronous, by
-  // using this helper we can attempt to let this process complete in a non
-  // blocking fashion before the TextureOwner is used.
-  //
-  // Clients should call this method to release the codec buffer for rendering
-  // and then call WaitForFrameAvailable() before using the TextureOwner. In
-  // the ideal case the TextureOwner has already been updated, otherwise the
-  // method will wait for a pro-rated amount of time based on elapsed time up
-  // to a short deadline.
-  //
-  // Some devices do not reliably notify frame availability, so we use a very
-  // short deadline of only a few milliseconds to avoid indefinite stalls.
-  void RenderCodecBufferToTextureOwner(MediaCodecBridge* codec,
-                                       int codec_buffer_index);
-
-  void WaitForFrameAvailable();
-
-  // Helper methods for interacting with |texture_owner_|. See
-  // gl::TextureOwner for method details.
-  void UpdateTexImage();
-
-  // Returns a matrix that needs to be y flipped in order to match the
-  // StreamTextureMatrix contract. See GLStreamTextureImage::YInvertMatrix().
-  void GetTransformMatrix(float matrix[16]) const;
-
-  // Resets the last time for RenderCodecBufferToTextureOwner(). Should be
-  // called during codec changes.
-  void ClearReleaseTime();
-
-  void ClearOverlay(AndroidOverlay* overlay);
-
-  void SetPromotionHintCB(PromotionHintAggregator::NotifyPromotionHintCB cb);
-  const PromotionHintAggregator::NotifyPromotionHintCB& GetPromotionHintCB();
-
- protected:
-  virtual ~AVDASharedState();
-
- private:
-  friend class base::RefCounted<AVDASharedState>;
-
-  // Texture matrix of the front buffer of the texture owner.
-  float gl_matrix_[16];
-
-  scoped_refptr<AVDASurfaceBundle> surface_bundle_;
-
-  PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb_;
-
-  base::WeakPtrFactory<AVDASharedState> weak_this_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(AVDASharedState);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_ANDROID_AVDA_SHARED_STATE_H_
diff --git a/media/gpu/android/avda_state_provider.h b/media/gpu/android/avda_state_provider.h
deleted file mode 100644
index 4ae1dde..0000000
--- a/media/gpu/android/avda_state_provider.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_ANDROID_AVDA_STATE_PROVIDER_H_
-#define MEDIA_GPU_ANDROID_AVDA_STATE_PROVIDER_H_
-
-#include "base/compiler_specific.h"
-#include "base/threading/thread_checker.h"
-#include "gpu/command_buffer/service/texture_manager.h"
-#include "media/gpu/android/promotion_hint_aggregator.h"
-#include "media/video/video_decode_accelerator.h"
-
-namespace gpu {
-namespace gles2 {
-class ContextGroup;
-}
-}  // namespace gpu
-
-namespace media {
-
-// Helper class that provides AVDAPictureBufferManager with enough state
-// to do useful work.
-class AVDAStateProvider {
- public:
-  // Various handy getters.
-  virtual const gfx::Size& GetSize() const = 0;
-  virtual gpu::gles2::ContextGroup* GetContextGroup() const = 0;
-  virtual std::unique_ptr<gpu::gles2::AbstractTexture> CreateAbstractTexture(
-      GLenum target,
-      GLenum internal_format,
-      GLsizei width,
-      GLsizei height,
-      GLsizei depth,
-      int border,
-      GLenum format,
-      GLenum type) = 0;
-
-  // Report a fatal error. This will post NotifyError(), and transition to the
-  // error state.
-  virtual void NotifyError(VideoDecodeAccelerator::Error error) = 0;
-
-  // Return a callback that may be used to signal promotion hint info.
-  virtual PromotionHintAggregator::NotifyPromotionHintCB
-  GetPromotionHintCB() = 0;
-
- protected:
-  ~AVDAStateProvider() = default;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_ANDROID_AVDA_STATE_PROVIDER_H_
diff --git a/media/gpu/android/codec_allocator.cc b/media/gpu/android/codec_allocator.cc
index ef5f3c3..5d3660d 100644
--- a/media/gpu/android/codec_allocator.cc
+++ b/media/gpu/android/codec_allocator.cc
@@ -23,7 +23,6 @@
 #include "media/base/limits.h"
 #include "media/base/media.h"
 #include "media/base/timestamp_constants.h"
-#include "media/gpu/android/android_video_decode_accelerator.h"
 
 namespace media {
 
diff --git a/media/gpu/gpu_video_decode_accelerator_factory.cc b/media/gpu/gpu_video_decode_accelerator_factory.cc
index dc9ff3f..f9787be 100644
--- a/media/gpu/gpu_video_decode_accelerator_factory.cc
+++ b/media/gpu/gpu_video_decode_accelerator_factory.cc
@@ -28,12 +28,6 @@
 #include "media/gpu/v4l2/v4l2_video_decode_accelerator.h"
 #include "ui/gl/gl_surface_egl.h"
 #endif
-#if defined(OS_ANDROID)
-#include "media/gpu/android/android_video_decode_accelerator.h"
-#include "media/gpu/android/android_video_surface_chooser_impl.h"
-#include "media/gpu/android/codec_allocator.h"
-#include "media/gpu/android/device_info.h"
-#endif
 #if BUILDFLAG(USE_VAAPI)
 #include "media/gpu/vaapi/vaapi_video_decode_accelerator.h"
 #include "ui/gl/gl_implementation.h"
@@ -79,9 +73,6 @@
 #elif defined(OS_MACOSX)
   capabilities.supported_profiles =
       VTVideoDecodeAccelerator::GetSupportedProfiles();
-#elif defined(OS_ANDROID)
-  capabilities =
-      AndroidVideoDecodeAccelerator::GetCapabilities(gpu_preferences);
 #endif
 
   return GpuVideoAcceleratorUtil::ConvertMediaToGpuDecodeCapabilities(
@@ -172,9 +163,6 @@
 #if defined(OS_MACOSX)
     &GpuVideoDecodeAcceleratorFactory::CreateVTVDA,
 #endif
-#if defined(OS_ANDROID)
-    &GpuVideoDecodeAcceleratorFactory::CreateAndroidVDA,
-#endif
   };
 
   std::unique_ptr<VideoDecodeAccelerator> vda;
@@ -260,23 +248,6 @@
 }
 #endif
 
-#if defined(OS_ANDROID)
-std::unique_ptr<VideoDecodeAccelerator>
-GpuVideoDecodeAcceleratorFactory::CreateAndroidVDA(
-    const gpu::GpuDriverBugWorkarounds& workarounds,
-    const gpu::GpuPreferences& gpu_preferences,
-    MediaLog* media_log) const {
-  std::unique_ptr<VideoDecodeAccelerator> decoder;
-  decoder.reset(new AndroidVideoDecodeAccelerator(
-      CodecAllocator::GetInstance(base::ThreadTaskRunnerHandle::Get()),
-      std::make_unique<AndroidVideoSurfaceChooserImpl>(
-          DeviceInfo::GetInstance()->IsSetOutputSurfaceSupported()),
-      make_context_current_cb_, get_context_group_cb_, overlay_factory_cb_,
-      create_abstract_texture_cb_, DeviceInfo::GetInstance()));
-  return decoder;
-}
-#endif
-
 GpuVideoDecodeAcceleratorFactory::GpuVideoDecodeAcceleratorFactory(
     const GetGLContextCallback& get_gl_context_cb,
     const MakeGLContextCurrentCallback& make_context_current_cb,
diff --git a/media/mojo/README.md b/media/mojo/README.md
index 8e3e9bb..652e62be 100644
--- a/media/mojo/README.md
+++ b/media/mojo/README.md
@@ -200,7 +200,6 @@
 2. Remote media components hosted in `MediaService`, e.g. by
    `MojoRendererService`, `MojoAudioDecoderService` and
    `MojoVideoDecoderService`.
-3. Legacy remote media components like `AndroidVideoDecodeAccelerator`.
 
 At the JavaScript layer, the media player and MediaKeys are connected via
 [`setMediaKeys()`](https://w3c.github.io/encrypted-media/#dom-htmlmediaelement-setmediakeys).
@@ -223,13 +222,12 @@
 #### Using CdmContext
 
 In some cases the media component is set to work with a specific CDM. For
-example, on Android, MediaCodec-based decoders (e.g. `MediaCodecAudioDecoder`
-and `AndroidVideoDecodeAccelerator`) can only use MediaDrm-based CDM via
-`MediaCryptoContext`. The media component and the CDM must live in the same
-process because the interaction of these two are typically happening deep at the
-OS level. In theory, they can both live in the render process. But in practice,
-typically both the CDM and the media component are hosted by the MediaService in
-a remote (e.g. GPU) process.
+example, on Android, MediaCodec-based decoders (e.g. `MediaCodecAudioDecoder`)
+can only use MediaDrm-based CDM via `MediaCryptoContext`. The media component
+and the CDM must live in the same process because the interaction of these two
+are typically happening deep at the OS level. In theory, they can both live in
+the render process. But in practice, typically both the CDM and the media
+component are hosted by the MediaService in a remote (e.g. GPU) process.
 
 To be able to attach a remote CDM with a remote media component, each
 `InterfaceFactoryImpl` instance (corresponding to one `RenderFrame`) in the
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index d43ba94..f6f8fc6 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -91,7 +91,6 @@
     "//gpu/ipc/service",
     "//media",
     "//media:shared_memory_support",
-    "//media/cdm:cdm_manager",
     "//media/gpu",
     "//media/gpu:buildflags",
     "//media/gpu/ipc/service",
diff --git a/media/mojo/services/mojo_cdm_service.cc b/media/mojo/services/mojo_cdm_service.cc
index 1ccfd2f..e4ce751 100644
--- a/media/mojo/services/mojo_cdm_service.cc
+++ b/media/mojo/services/mojo_cdm_service.cc
@@ -16,7 +16,6 @@
 #include "media/base/cdm_factory.h"
 #include "media/base/cdm_key_information.h"
 #include "media/base/key_systems.h"
-#include "media/cdm/cdm_manager.h"
 #include "media/mojo/common/media_type_converters.h"
 #include "media/mojo/services/mojo_cdm_service_context.h"
 #include "url/origin.h"
@@ -49,7 +48,6 @@
   if (!context_ || cdm_id_ == CdmContext::kInvalidCdmId)
     return;
 
-  CdmManager::GetInstance()->UnregisterCdm(cdm_id_);
   context_->UnregisterCdm(cdm_id_);
 }
 
@@ -161,7 +159,6 @@
 
   if (context_) {
     cdm_id_ = context_->RegisterCdm(this);
-    CdmManager::GetInstance()->RegisterCdm(cdm_id_, cdm);
     DVLOG(1) << __func__ << ": CDM successfully registered with ID " << cdm_id_;
   }
 
diff --git a/media/remoting/media_remoting_rpc.proto b/media/remoting/media_remoting_rpc.proto
index 66d7db5..ebf5881 100644
--- a/media/remoting/media_remoting_rpc.proto
+++ b/media/remoting/media_remoting_rpc.proto
@@ -189,6 +189,8 @@
     AV1PROFILE_PROFILE_MAIN = 24;
     AV1PROFILE_PROFILE_HIGH = 25;
     AV1PROFILE_PROFILE_PRO = 26;
+    DOLBYVISION_PROFILE8 = 27;
+    DOLBYVISION_PROFILE9 = 28;
   };
 
   // Proto version of media::VideoPixelFormat.
diff --git a/media/remoting/proto_enum_utils.cc b/media/remoting/proto_enum_utils.cc
index 9f41d7f9..f592da04 100644
--- a/media/remoting/proto_enum_utils.cc
+++ b/media/remoting/proto_enum_utils.cc
@@ -284,6 +284,8 @@
     CASE_RETURN_OTHER(DOLBYVISION_PROFILE4);
     CASE_RETURN_OTHER(DOLBYVISION_PROFILE5);
     CASE_RETURN_OTHER(DOLBYVISION_PROFILE7);
+    CASE_RETURN_OTHER(DOLBYVISION_PROFILE8);
+    CASE_RETURN_OTHER(DOLBYVISION_PROFILE9);
     CASE_RETURN_OTHER(THEORAPROFILE_ANY);
     CASE_RETURN_OTHER(AV1PROFILE_PROFILE_MAIN);
     CASE_RETURN_OTHER(AV1PROFILE_PROFILE_HIGH);
@@ -321,6 +323,8 @@
     CASE_RETURN_OTHER(DOLBYVISION_PROFILE4);
     CASE_RETURN_OTHER(DOLBYVISION_PROFILE5);
     CASE_RETURN_OTHER(DOLBYVISION_PROFILE7);
+    CASE_RETURN_OTHER(DOLBYVISION_PROFILE8);
+    CASE_RETURN_OTHER(DOLBYVISION_PROFILE9);
     CASE_RETURN_OTHER(THEORAPROFILE_ANY);
     CASE_RETURN_OTHER(AV1PROFILE_PROFILE_MAIN);
     CASE_RETURN_OTHER(AV1PROFILE_PROFILE_HIGH);
diff --git a/mojo/core/channel.cc b/mojo/core/channel.cc
index 7daaaa1..14ad9aa 100644
--- a/mojo/core/channel.cc
+++ b/mojo/core/channel.cc
@@ -247,6 +247,11 @@
                 sizeof(MachPortsEntry);
 #else
   const uint32_t max_handles = 0;
+  // No extra header expected. Fail if this is detected.
+  if (extra_header_size > 0) {
+    DLOG(ERROR) << "Decoding invalid message: unexpected extra_header_size > 0";
+    return nullptr;
+  }
 #endif  // defined(OS_WIN)
 
   const uint16_t num_handles =
diff --git a/mojo/core/channel_unittest.cc b/mojo/core/channel_unittest.cc
index 14585e3..14db631 100644
--- a/mojo/core/channel_unittest.cc
+++ b/mojo/core/channel_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/process/process_handle.h"
 #include "base/run_loop.h"
 #include "base/threading/thread.h"
+#include "build/build_config.h"
 #include "mojo/core/platform_handle_utils.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -364,6 +365,28 @@
                                                    base::kNullProcessHandle));
 }
 
+#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_FUCHSIA)
+TEST(ChannelTest, DeserializeMessage_NonZeroExtraHeaderSize) {
+  // Verifies that a message payload is rejected when the extra header chunk
+  // size anything but zero on Linux, even if it's aligned.
+  constexpr uint16_t kTotalHeaderSize =
+      sizeof(Channel::Message::Header) + kChannelMessageAlignment;
+  constexpr uint32_t kEmptyPayloadSize = 8;
+  constexpr uint32_t kMessageSize = kTotalHeaderSize + kEmptyPayloadSize;
+  char message[kMessageSize];
+  memset(message, 0, kMessageSize);
+
+  Channel::Message::Header* header =
+      reinterpret_cast<Channel::Message::Header*>(&message[0]);
+  header->num_bytes = kMessageSize;
+  header->num_header_bytes = kTotalHeaderSize;
+  header->message_type = Channel::Message::MessageType::NORMAL;
+  header->num_handles = 0;
+  EXPECT_EQ(nullptr, Channel::Message::Deserialize(&message[0], kMessageSize,
+                                                   base::kNullProcessHandle));
+}
+#endif
+
 class CountingChannelDelegate : public Channel::Delegate {
  public:
   explicit CountingChannelDelegate(base::OnceClosure on_final_message)
diff --git a/net/BUILD.gn b/net/BUILD.gn
index b554414..edfcdc1 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2664,8 +2664,6 @@
     "data/ssl/certificates/treadclimber.sctlist",
     "data/ssl/certificates/unescaped.pem",
     "data/ssl/certificates/unittest.key.bin",
-    "data/ssl/certificates/unittest.originbound.der",
-    "data/ssl/certificates/unittest.originbound.key.der",
     "data/ssl/certificates/unittest.selfsigned.der",
     "data/ssl/certificates/verisign_class3_g5_crosssigned-trusted.keychain",
     "data/ssl/certificates/verisign_class3_g5_crosssigned.pem",
@@ -6351,6 +6349,19 @@
   ]
 }
 
+fuzzer_test("net_crl_set_fuzzer") {
+  sources = [
+    "cert/crl_set_fuzzer.cc",
+  ]
+  deps = [
+    ":net_fuzzer_test_support",
+    ":test_support",
+    "//base",
+    "//net",
+  ]
+  seed_corpus = "data/fuzzer_data/net_crl_set_fuzzer/"
+}
+
 if (!disable_ftp_support) {
   fuzzer_test("net_ftp_ctrl_response_fuzzer") {
     sources = [
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 484db21..8eb68e8b 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -40,6 +40,7 @@
 #include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util.h"
 #include "net/cert_net/cert_net_fetcher_impl.h"
+#include "net/der/encode_values.h"
 #include "net/der/input.h"
 #include "net/der/parser.h"
 #include "net/proxy_resolution/proxy_config.h"
@@ -177,6 +178,15 @@
     false;
 #endif
 
+// Wrapper for base::mac::IsAtLeastOS10_12() to avoid littering ifdefs.
+bool IsMacAtLeastOS10_12() {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  return base::mac::IsAtLeastOS10_12();
+#else
+  return false;
+#endif
+}
+
 // Returns a textual description of the CertVerifyProc implementation
 // that is being tested, used to give better names to parameterized
 // tests.
@@ -238,6 +248,16 @@
 #endif
 }
 
+// Helper to make creating an X509Certificate chain less verbose.
+scoped_refptr<X509Certificate> CreateX509CertificateWithIntermediate(
+    bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer,
+    bssl::UniquePtr<CRYPTO_BUFFER> intermediate_buffer) {
+  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
+  intermediates.push_back(std::move(intermediate_buffer));
+  return X509Certificate::CreateFromBuffer(std::move(cert_buffer),
+                                           std::move(intermediates));
+}
+
 std::string MakeRandomHexString(size_t num_bytes) {
   std::vector<char> rand_bytes;
   rand_bytes.resize(num_bytes);
@@ -246,6 +266,65 @@
   return base::HexEncode(&rand_bytes[0], rand_bytes.size());
 }
 
+std::string Sha256WithRSAEncryption() {
+  const uint8_t kSha256WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a,
+                                              0x86, 0x48, 0x86, 0xf7, 0x0d,
+                                              0x01, 0x01, 0x0b, 0x05, 0x00};
+  return std::string(std::begin(kSha256WithRSAEncryption),
+                     std::end(kSha256WithRSAEncryption));
+}
+
+// Adds bytes (specified as a StringPiece) to the given CBB.
+// The argument ordering follows the boringssl CBB_* api style.
+bool CBBAddBytes(CBB* cbb, base::StringPiece bytes) {
+  return CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(bytes.data()),
+                       bytes.size());
+}
+
+// Adds bytes (from fixed size array) to the given CBB.
+// The argument ordering follows the boringssl CBB_* api style.
+template <size_t N>
+bool CBBAddBytes(CBB* cbb, const uint8_t (&data)[N]) {
+  return CBB_add_bytes(cbb, data, N);
+}
+
+// Adds a RFC 5280 Time value to the given CBB.
+// The argument ordering follows the boringssl CBB_* api style.
+bool CBBAddTime(CBB* cbb, const base::Time& time) {
+  der::GeneralizedTime generalized_time;
+  if (!der::EncodeTimeAsGeneralizedTime(time, &generalized_time))
+    return false;
+  CBB time_cbb;
+  if (generalized_time.year < 2050) {
+    uint8_t out[der::kUTCTimeLength];
+    if (!der::EncodeUTCTime(generalized_time, out) ||
+        !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_UTCTIME) ||
+        !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb))
+      return false;
+  } else {
+    uint8_t out[der::kGeneralizedTimeLength];
+    if (!der::EncodeGeneralizedTime(generalized_time, out) ||
+        !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_GENERALIZEDTIME) ||
+        !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb))
+      return false;
+  }
+  return true;
+}
+
+// Finalizes the CBB to a std::string.
+std::string FinishCBB(CBB* cbb) {
+  size_t cbb_len;
+  uint8_t* cbb_bytes;
+
+  if (!CBB_finish(cbb, &cbb_bytes, &cbb_len)) {
+    ADD_FAILURE() << "CBB_finish() failed";
+    return std::string();
+  }
+
+  bssl::UniquePtr<uint8_t> delete_bytes(cbb_bytes);
+  return std::string(reinterpret_cast<char*>(cbb_bytes), cbb_len);
+}
+
 // CertBuilder is a helper class to dynamically create a test certificate.
 //
 // CertBuilder is initialized using an existing certificate, from which it
@@ -279,6 +358,13 @@
     Invalidate();
   }
 
+  // Removes an extension (if present).
+  void EraseExtension(const der::Input& oid) {
+    extensions_.erase(oid.AsString());
+
+    Invalidate();
+  }
+
   // Sets an AIA extension with a single caIssuers access method.
   void SetCaIssuersUrl(const GURL& url) {
     std::string url_spec = url.spec();
@@ -297,15 +383,49 @@
     ASSERT_TRUE(CBB_add_asn1(cbb.get(), &aia, CBS_ASN1_SEQUENCE));
     ASSERT_TRUE(CBB_add_asn1(&aia, &ca_issuer, CBS_ASN1_SEQUENCE));
     ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_method, CBS_ASN1_OBJECT));
-    ASSERT_TRUE(
-        AddBytesToCBB(&access_method, AdCaIssuersOid().AsStringPiece()));
+    ASSERT_TRUE(CBBAddBytes(&access_method, AdCaIssuersOid().AsStringPiece()));
     ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_location,
                              CBS_ASN1_CONTEXT_SPECIFIC | 6));
-    ASSERT_TRUE(AddBytesToCBB(&access_location, url_spec));
+    ASSERT_TRUE(CBBAddBytes(&access_location, url_spec));
 
     SetExtension(AuthorityInfoAccessOid(), FinishCBB(cbb.get()));
   }
 
+  void SetCrlDistributionPointUrl(const GURL& url) {
+    std::string url_spec = url.spec();
+
+    bssl::ScopedCBB cbb;
+    ASSERT_TRUE(CBB_init(cbb.get(), url_spec.size()));
+    CBB dps, dp, dp_name, dp_fullname, dp_url;
+
+    //    CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+    ASSERT_TRUE(CBB_add_asn1(cbb.get(), &dps, CBS_ASN1_SEQUENCE));
+
+    //    DistributionPoint ::= SEQUENCE {
+    //         distributionPoint       [0]     DistributionPointName OPTIONAL,
+    //         reasons                 [1]     ReasonFlags OPTIONAL,
+    //         cRLIssuer               [2]     GeneralNames OPTIONAL }
+    ASSERT_TRUE(CBB_add_asn1(&dps, &dp, CBS_ASN1_SEQUENCE));
+    ASSERT_TRUE(CBB_add_asn1(
+        &dp, &dp_name, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+
+    //    DistributionPointName ::= CHOICE {
+    //         fullName                [0]     GeneralNames,
+    //         nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
+    ASSERT_TRUE(
+        CBB_add_asn1(&dp_name, &dp_fullname,
+                     CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+
+    //   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+    //   GeneralName ::= CHOICE {
+    // uniformResourceIdentifier       [6]     IA5String,
+    ASSERT_TRUE(
+        CBB_add_asn1(&dp_fullname, &dp_url, CBS_ASN1_CONTEXT_SPECIFIC | 6));
+    ASSERT_TRUE(CBBAddBytes(&dp_url, url_spec));
+
+    SetExtension(CrlDistributionPointsOid(), FinishCBB(cbb.get()));
+  }
+
   // Sets the SAN for the certificate to a single dNSName.
   void SetSubjectAltName(const std::string& dns_name) {
     // From RFC 5280:
@@ -325,7 +445,7 @@
     ASSERT_TRUE(CBB_add_asn1(cbb.get(), &general_names, CBS_ASN1_SEQUENCE));
     ASSERT_TRUE(CBB_add_asn1(&general_names, &general_name,
                              CBS_ASN1_CONTEXT_SPECIFIC | 2));
-    ASSERT_TRUE(AddBytesToCBB(&general_name, dns_name));
+    ASSERT_TRUE(CBBAddBytes(&general_name, dns_name));
 
     SetExtension(SubjectAltNameOid(), FinishCBB(cbb.get()));
   }
@@ -335,11 +455,7 @@
   void SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest) {
     switch (digest) {
       case DigestAlgorithm::Sha256: {
-        const uint8_t kSha256WithRSAEncryption[] = {
-            0x30, 0x0D, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
-            0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00};
-        SetSignatureAlgorithm(std::string(std::begin(kSha256WithRSAEncryption),
-                                          std::end(kSha256WithRSAEncryption)));
+        SetSignatureAlgorithm(Sha256WithRSAEncryption());
         break;
       }
 
@@ -385,6 +501,13 @@
     return subject_tlv_;
   }
 
+  // Returns the serial number for the generated certificate.
+  uint64_t GetSerialNumber() {
+    if (!serial_number_)
+      serial_number_ = base::RandUint64();
+    return serial_number_;
+  }
+
   // Returns the (RSA) key for the generated certificate.
   EVP_PKEY* GetKey() {
     if (!key_)
@@ -415,26 +538,6 @@
     key_ = bssl::UpRef(private_key->key());
   }
 
-  // Adds bytes (specified as a StringPiece) to the given CBB.
-  static bool AddBytesToCBB(CBB* cbb, base::StringPiece bytes) {
-    return CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(bytes.data()),
-                         bytes.size());
-  }
-
-  // Finalizes the CBB to a std::string.
-  static std::string FinishCBB(CBB* cbb) {
-    size_t cbb_len;
-    uint8_t* cbb_bytes;
-
-    if (!CBB_finish(cbb, &cbb_bytes, &cbb_len)) {
-      ADD_FAILURE() << "CBB_finish() failed";
-      return std::string();
-    }
-
-    bssl::UniquePtr<uint8_t> delete_bytes(cbb_bytes);
-    return std::string(reinterpret_cast<char*>(cbb_bytes), cbb_len);
-  }
-
   // Generates a random subject for the certificate, comprised of just a CN.
   void GenerateSubject() {
     ASSERT_TRUE(subject_tlv_.empty());
@@ -453,20 +556,13 @@
     ASSERT_TRUE(CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET));
     ASSERT_TRUE(CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE));
     ASSERT_TRUE(CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT));
-    ASSERT_TRUE(CBB_add_bytes(&type, kCommonName, sizeof(kCommonName)));
+    ASSERT_TRUE(CBBAddBytes(&type, kCommonName));
     ASSERT_TRUE(CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING));
-    ASSERT_TRUE(AddBytesToCBB(&value, common_name));
+    ASSERT_TRUE(CBBAddBytes(&value, common_name));
 
     subject_tlv_ = FinishCBB(cbb.get());
   }
 
-  // Returns the serial number for the generated certificate.
-  uint64_t GetSerialNumber() {
-    if (!serial_number_)
-      serial_number_ = base::RandUint64();
-    return serial_number_;
-  }
-
   // Parses |cert| and copies the following properties:
   //   * All extensions (dropping any duplicates)
   //   * Signature algorithm (from Certificate)
@@ -564,9 +660,9 @@
     ASSERT_TRUE(CBB_add_asn1_uint64(&version, 2));
     ASSERT_TRUE(CBB_add_asn1_uint64(&tbs_cert, GetSerialNumber()));
     ASSERT_TRUE(AddSignatureAlgorithm(&tbs_cert));
-    ASSERT_TRUE(AddBytesToCBB(&tbs_cert, issuer_->GetSubject()));
-    ASSERT_TRUE(AddBytesToCBB(&tbs_cert, validity_tlv_));
-    ASSERT_TRUE(AddBytesToCBB(&tbs_cert, GetSubject()));
+    ASSERT_TRUE(CBBAddBytes(&tbs_cert, issuer_->GetSubject()));
+    ASSERT_TRUE(CBBAddBytes(&tbs_cert, validity_tlv_));
+    ASSERT_TRUE(CBBAddBytes(&tbs_cert, GetSubject()));
     ASSERT_TRUE(EVP_marshal_public_key(&tbs_cert, GetKey()));
 
     // Serialize all the extensions.
@@ -590,14 +686,14 @@
         ASSERT_TRUE(
             CBB_add_asn1(&extensions, &extension_seq, CBS_ASN1_SEQUENCE));
         ASSERT_TRUE(CBB_add_asn1(&extension_seq, &oid, CBS_ASN1_OBJECT));
-        ASSERT_TRUE(AddBytesToCBB(&oid, extension_it.first));
+        ASSERT_TRUE(CBBAddBytes(&oid, extension_it.first));
         if (extension_it.second.critical) {
           ASSERT_TRUE(CBB_add_asn1_bool(&extension_seq, true));
         }
 
         ASSERT_TRUE(
             CBB_add_asn1(&extension_seq, &extn_value, CBS_ASN1_OCTETSTRING));
-        ASSERT_TRUE(AddBytesToCBB(&extn_value, extension_it.second.value));
+        ASSERT_TRUE(CBBAddBytes(&extn_value, extension_it.second.value));
         ASSERT_TRUE(CBB_flush(&extensions));
       }
     }
@@ -606,7 +702,7 @@
   }
 
   bool AddSignatureAlgorithm(CBB* cbb) {
-    return AddBytesToCBB(cbb, signature_algorithm_tlv_);
+    return CBBAddBytes(cbb, signature_algorithm_tlv_);
   }
 
   void GenerateCertificate() {
@@ -649,7 +745,7 @@
 
     ASSERT_TRUE(CBB_init(cbb.get(), tbs_cert.size()));
     ASSERT_TRUE(CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE));
-    ASSERT_TRUE(AddBytesToCBB(&cert, tbs_cert));
+    ASSERT_TRUE(CBBAddBytes(&cert, tbs_cert));
     ASSERT_TRUE(AddSignatureAlgorithm(&cert));
     ASSERT_TRUE(CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING));
     ASSERT_TRUE(CBB_add_u8(&signature, 0 /* no unused bits */));
@@ -773,6 +869,19 @@
            verify_proc_type() == CERT_VERIFY_PROC_BUILTIN;
   }
 
+  bool SupportsSoftFailRevChecking() const {
+    return verify_proc_type() == CERT_VERIFY_PROC_NSS ||
+           verify_proc_type() == CERT_VERIFY_PROC_WIN ||
+           verify_proc_type() == CERT_VERIFY_PROC_MAC ||
+           verify_proc_type() == CERT_VERIFY_PROC_BUILTIN;
+  }
+
+  bool SupportsRevCheckingRequiredLocalAnchors() const {
+    return verify_proc_type() == CERT_VERIFY_PROC_NSS ||
+           verify_proc_type() == CERT_VERIFY_PROC_WIN ||
+           verify_proc_type() == CERT_VERIFY_PROC_BUILTIN;
+  }
+
   CertVerifyProc* verify_proc() const { return verify_proc_.get(); }
 
  private:
@@ -2806,14 +2915,17 @@
   // Registers a handler with the test server that responds with the given
   // Content-Type, HTTP status code, and response body, for GET requests
   // to |path|.
-  void RegisterSimpleTestServerHandler(std::string path,
+  // Returns the full URL to |path| for the current test server.
+  GURL RegisterSimpleTestServerHandler(std::string path,
                                        HttpStatusCode status_code,
                                        std::string content_type,
                                        std::string content) {
+    GURL handler_url(GetTestServerAbsoluteUrl(path));
     base::AutoLock lock(request_handlers_lock_);
     request_handlers_.push_back(base::BindRepeating(
         &SimpleTestServerHandler, std::move(path), status_code,
         std::move(content_type), std::move(content)));
+    return handler_url;
   }
 
   // Returns a random URL path (starting with /) that has the given suffix.
@@ -2826,15 +2938,14 @@
     return test_server_.GetURL(path);
   }
 
-  // Creates a certificate chain for www.example.com, where the leaf certificate
-  // has an AIA URL pointing to the test server.
-  void CreateSimpleChainWithAIA(
-      scoped_refptr<X509Certificate>* out_leaf,
-      std::string* ca_issuers_path,
-      bssl::UniquePtr<CRYPTO_BUFFER>* out_intermediate,
-      scoped_refptr<X509Certificate>* out_root) {
+  // Creates a simple leaf->intermediate->root chain of CertBuilders with no AIA
+  // or CrlDistributionPoint extensions, and leaf having a subjectAltName of
+  // www.example.com.
+  static void CreateSimpleCertBuilderChain(
+      std::unique_ptr<CertBuilder>* out_leaf,
+      std::unique_ptr<CertBuilder>* out_intermediate,
+      std::unique_ptr<CertBuilder>* out_root) {
     const char kHostname[] = "www.example.com";
-
     base::FilePath certs_dir =
         GetTestNetDataDirectory()
             .AppendASCII("verify_certificate_chain_unittest")
@@ -2844,24 +2955,142 @@
         certs_dir, "chain.pem", X509Certificate::FORMAT_AUTO);
     ASSERT_EQ(3U, orig_certs.size());
 
-    // Build a slightly modified variant of |orig_certs|.
-    CertBuilder root(orig_certs[2]->cert_buffer(), nullptr);
-    CertBuilder intermediate(orig_certs[1]->cert_buffer(), &root);
-    CertBuilder leaf(orig_certs[0]->cert_buffer(), &intermediate);
+    // Build slightly modified variants of |orig_certs|.
+    *out_root =
+        std::make_unique<CertBuilder>(orig_certs[2]->cert_buffer(), nullptr);
+    *out_intermediate = std::make_unique<CertBuilder>(
+        orig_certs[1]->cert_buffer(), out_root->get());
+    (*out_intermediate)->EraseExtension(CrlDistributionPointsOid());
+    (*out_intermediate)->EraseExtension(AuthorityInfoAccessOid());
+    *out_leaf = std::make_unique<CertBuilder>(orig_certs[0]->cert_buffer(),
+                                              out_intermediate->get());
+    (*out_leaf)->SetSubjectAltName(kHostname);
+    (*out_leaf)->EraseExtension(CrlDistributionPointsOid());
+    (*out_leaf)->EraseExtension(AuthorityInfoAccessOid());
+  }
+
+  // Creates a certificate chain for www.example.com, where the leaf certificate
+  // has an AIA URL pointing to the test server.
+  void CreateSimpleChainWithAIA(
+      scoped_refptr<X509Certificate>* out_leaf,
+      std::string* ca_issuers_path,
+      bssl::UniquePtr<CRYPTO_BUFFER>* out_intermediate,
+      scoped_refptr<X509Certificate>* out_root) {
+    std::unique_ptr<CertBuilder> leaf, intermediate, root;
+    CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+    ASSERT_TRUE(leaf && intermediate && root);
 
     // Make the leaf certificate have an AIA (CA Issuers) that points to the
     // embedded test server. This uses a random URL for predictable behavior in
     // the presence of global caching.
     *ca_issuers_path = MakeRandomPath(".cer");
     GURL ca_issuers_url = GetTestServerAbsoluteUrl(*ca_issuers_path);
-    leaf.SetCaIssuersUrl(ca_issuers_url);
-    leaf.SetSubjectAltName(kHostname);
+    leaf->SetCaIssuersUrl(ca_issuers_url);
 
     // The chain being verified is solely the leaf certificate (missing the
     // intermediate and root).
-    *out_leaf = leaf.GetX509Certificate();
-    *out_root = root.GetX509Certificate();
-    *out_intermediate = intermediate.DupCertBuffer();
+    *out_leaf = leaf->GetX509Certificate();
+    *out_root = root->GetX509Certificate();
+    *out_intermediate = intermediate->DupCertBuffer();
+  }
+
+  // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials|
+  // as revoked.
+  // Returns the DER-encoded CRL.
+  static std::string CreateCrl(CertBuilder* crl_issuer,
+                               const std::vector<uint64_t>& revoked_serials) {
+    //    TBSCertList  ::=  SEQUENCE  {
+    //         version                 Version OPTIONAL,
+    //                                      -- if present, MUST be v2
+    //         signature               AlgorithmIdentifier,
+    //         issuer                  Name,
+    //         thisUpdate              Time,
+    //         nextUpdate              Time OPTIONAL,
+    //         revokedCertificates     SEQUENCE OF SEQUENCE  {
+    //              userCertificate         CertificateSerialNumber,
+    //              revocationDate          Time,
+    //              crlEntryExtensions      Extensions OPTIONAL
+    //                                       -- if present, version MUST be v2
+    //                                   }  OPTIONAL,
+    //         crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
+    //                                       -- if present, version MUST be v2
+    //                                   }
+    bssl::ScopedCBB tbs_cbb;
+    CBB tbs_cert_list, revoked_serials_cbb;
+    if (!CBB_init(tbs_cbb.get(), 10) ||
+        !CBB_add_asn1(tbs_cbb.get(), &tbs_cert_list, CBS_ASN1_SEQUENCE) ||
+        !CBB_add_asn1_uint64(&tbs_cert_list, 1 /* V2 */) ||
+        !CBBAddBytes(&tbs_cert_list, Sha256WithRSAEncryption()) ||
+        !CBBAddBytes(&tbs_cert_list, crl_issuer->GetSubject()) ||
+        !CBBAddTime(&tbs_cert_list,
+                    base::Time::Now() - base::TimeDelta::FromDays(1)) ||
+        !CBBAddTime(&tbs_cert_list,
+                    base::Time::Now() + base::TimeDelta::FromDays(6))) {
+      ADD_FAILURE();
+      return std::string();
+    }
+    if (!revoked_serials.empty()) {
+      if (!CBB_add_asn1(&tbs_cert_list, &revoked_serials_cbb,
+                        CBS_ASN1_SEQUENCE)) {
+        ADD_FAILURE();
+        return std::string();
+      }
+      for (const int64_t revoked_serial : revoked_serials) {
+        CBB revoked_serial_cbb;
+        if (!CBB_add_asn1(&revoked_serials_cbb, &revoked_serial_cbb,
+                          CBS_ASN1_SEQUENCE) ||
+            !CBB_add_asn1_uint64(&revoked_serial_cbb, revoked_serial) ||
+            !CBBAddTime(&revoked_serial_cbb,
+                        base::Time::Now() - base::TimeDelta::FromDays(1)) ||
+            !CBB_flush(&revoked_serials_cbb)) {
+          ADD_FAILURE();
+          return std::string();
+        }
+      }
+    }
+
+    std::string tbs_tlv = FinishCBB(tbs_cbb.get());
+
+    //    CertificateList  ::=  SEQUENCE  {
+    //         tbsCertList          TBSCertList,
+    //         signatureAlgorithm   AlgorithmIdentifier,
+    //         signatureValue       BIT STRING  }
+    bssl::ScopedCBB crl_cbb;
+    CBB cert_list, signature;
+    bssl::ScopedEVP_MD_CTX ctx;
+    uint8_t* sig_out;
+    size_t sig_len;
+    if (!CBB_init(crl_cbb.get(), 10) ||
+        !CBB_add_asn1(crl_cbb.get(), &cert_list, CBS_ASN1_SEQUENCE) ||
+        !CBBAddBytes(&cert_list, tbs_tlv) ||
+        !CBBAddBytes(&cert_list, Sha256WithRSAEncryption()) ||
+        !CBB_add_asn1(&cert_list, &signature, CBS_ASN1_BITSTRING) ||
+        !CBB_add_u8(&signature, 0 /* no unused bits */) ||
+        !EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr,
+                            crl_issuer->GetKey()) ||
+        !EVP_DigestSign(ctx.get(), nullptr, &sig_len,
+                        reinterpret_cast<const uint8_t*>(tbs_tlv.data()),
+                        tbs_tlv.size()) ||
+        !CBB_reserve(&signature, &sig_out, sig_len) ||
+        !EVP_DigestSign(ctx.get(), sig_out, &sig_len,
+                        reinterpret_cast<const uint8_t*>(tbs_tlv.data()),
+                        tbs_tlv.size()) ||
+        !CBB_did_write(&signature, sig_len)) {
+      ADD_FAILURE();
+      return std::string();
+    }
+    return FinishCBB(crl_cbb.get());
+  }
+
+  // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials|
+  // as revoked, and registers it to be served by the test server.
+  // Returns the full URL to retrieve the CRL from the test server.
+  GURL CreateAndServeCrl(CertBuilder* crl_issuer,
+                         const std::vector<uint64_t>& revoked_serials) {
+    std::string crl = CreateCrl(crl_issuer, revoked_serials);
+    std::string crl_path = MakeRandomPath(".crl");
+    return RegisterSimpleTestServerHandler(crl_path, HTTP_OK,
+                                           "application/pkix-crl", crl);
   }
 
  private:
@@ -3244,6 +3473,643 @@
   }
 }
 
+TEST_P(CertVerifyProcInternalWithNetFetchingTest, RevocationHardFailNoCrls) {
+  if (!SupportsRevCheckingRequiredLocalAnchors()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS";
+    return;
+  }
+
+  // Create certs which have no AIA or CRL distribution points.
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with hard-fail revocation checking for local anchors.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  // "Hard fail" handling varies for certificates that have no revocation
+  // mechanisms at all. Some implementations consider it okay, some consider it
+  // a failure.
+  if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN ||
+      verify_proc_type() == CERT_VERIFY_PROC_WIN) {
+    EXPECT_THAT(error, IsOk());
+  } else {
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+// CRL hard fail test where both leaf and intermediate are covered by valid
+// CRLs which have empty (non-present) revokedCertificates list. Verification
+// should succeed.
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationHardFailCrlGoodNoRevokedCertificates) {
+  if (!SupportsRevCheckingRequiredLocalAnchors()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Serve a root-issued CRL which does not revoke intermediate.
+  intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {}));
+
+  // Serve an intermediate-issued CRL which does not revoke leaf.
+  leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with hard-fail revocation checking for local anchors.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  // Should pass, leaf and intermediate were covered by CRLs and were not
+  // revoked.
+  EXPECT_THAT(error, IsOk());
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+// CRL hard fail test where both leaf and intermediate are covered by valid
+// CRLs which have revokedCertificates lists that revoke other irrelevant
+// serial numbers. Verification should succeed.
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationHardFailCrlGoodIrrelevantSerialsRevoked) {
+  if (!SupportsRevCheckingRequiredLocalAnchors()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Root-issued CRL revokes leaf's serial number. This is irrelevant.
+  intermediate->SetCrlDistributionPointUrl(
+      CreateAndServeCrl(root.get(), {leaf->GetSerialNumber()}));
+
+  // Intermediate-issued CRL revokes intermediates's serial number. This is
+  // irrelevant.
+  leaf->SetCrlDistributionPointUrl(
+      CreateAndServeCrl(intermediate.get(), {intermediate->GetSerialNumber()}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with hard-fail revocation checking for local anchors.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  // Should pass, leaf and intermediate were covered by CRLs and were not
+  // revoked.
+  EXPECT_THAT(error, IsOk());
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationHardFailLeafRevokedByCrl) {
+  if (!SupportsRevCheckingRequiredLocalAnchors()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Root-issued CRL which does not revoke intermediate.
+  intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {}));
+
+  // Leaf is revoked by intermediate issued CRL.
+  leaf->SetCrlDistributionPointUrl(
+      CreateAndServeCrl(intermediate.get(), {leaf->GetSerialNumber()}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with hard-fail revocation checking for local anchors.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  // Should fail, leaf is revoked.
+  if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) {
+    // TODO(mattm): CertVerifyProcBuiltin CRL handling.
+    EXPECT_THAT(error, IsOk());
+  } else {
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationHardFailIntermediateRevokedByCrl) {
+  if (!SupportsRevCheckingRequiredLocalAnchors()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Intermediate is revoked by root issued CRL.
+  intermediate->SetCrlDistributionPointUrl(
+      CreateAndServeCrl(root.get(), {intermediate->GetSerialNumber()}));
+
+  // Intermediate-issued CRL which does not revoke leaf.
+  leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with hard-fail revocation checking for local anchors.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  // Should fail, intermediate is revoked.
+  if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) {
+    // TODO(mattm): CertVerifyProcBuiltin CRL handling.
+    EXPECT_THAT(error, IsOk());
+  } else {
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+// CRL hard fail test where the intermediate certificate has a good CRL, but
+// the leaf's distribution point returns an http error. Verification should
+// fail.
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationHardFailLeafCrlDpHttpError) {
+  if (!SupportsRevCheckingRequiredLocalAnchors()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Serve a root-issued CRL which does not revoke intermediate.
+  intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {}));
+
+  // Serve a 404 for the intermediate-issued CRL distribution point url.
+  leaf->SetCrlDistributionPointUrl(RegisterSimpleTestServerHandler(
+      MakeRandomPath(".crl"), HTTP_NOT_FOUND, "text/plain", "Not Found"));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with hard-fail revocation checking for local anchors.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  // Should fail since no revocation information was available for the leaf.
+  if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) {
+    // TODO(mattm): CertVerifyProcBuiltin CRL handling.
+    EXPECT_THAT(error, IsOk());
+  } else {
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+// CRL hard fail test where the leaf certificate has a good CRL, but
+// the intermediate's distribution point returns an http error. Verification
+// should fail.
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationHardFailIntermediateCrlDpHttpError) {
+  if (!SupportsRevCheckingRequiredLocalAnchors()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Serve a 404 for the root-issued CRL distribution point url.
+  intermediate->SetCrlDistributionPointUrl(RegisterSimpleTestServerHandler(
+      MakeRandomPath(".crl"), HTTP_NOT_FOUND, "text/plain", "Not Found"));
+
+  // Serve an intermediate-issued CRL which does not revoke leaf.
+  leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with hard-fail revocation checking for local anchors.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  // Should fail since no revocation information was available for the
+  // intermediate.
+  if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) {
+    // TODO(mattm): CertVerifyProcBuiltin CRL handling.
+    EXPECT_THAT(error, IsOk());
+  } else {
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+TEST_P(CertVerifyProcInternalWithNetFetchingTest, RevocationSoftFailNoCrls) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  // Create certs which have no AIA or CRL distribution points.
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_MAC && IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else {
+    // Soft-fail revocation checking should succeed when no revocation mechanism
+    // is available.
+    EXPECT_THAT(error, IsOk());
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+// CRL soft fail test where both leaf and intermediate are covered by valid
+// CRLs which have empty (non-present) revokedCertificates list. Verification
+// should succeed.
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationSoftFailCrlGoodNoRevokedCertificates) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Serve a root-issued CRL which does not revoke intermediate.
+  intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {}));
+
+  // Serve an intermediate-issued CRL which does not revoke leaf.
+  leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_MAC && IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else {
+    // Should pass, leaf and intermediate were covered by CRLs and were not
+    // revoked.
+    EXPECT_THAT(error, IsOk());
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+// CRL soft fail test where both leaf and intermediate are covered by valid
+// CRLs which have revokedCertificates lists that revoke other irrelevant
+// serial numbers. Verification should succeed.
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationSoftFailCrlGoodIrrelevantSerialsRevoked) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Root-issued CRL revokes leaf's serial number. This is irrelevant.
+  intermediate->SetCrlDistributionPointUrl(
+      CreateAndServeCrl(root.get(), {leaf->GetSerialNumber()}));
+
+  // Intermediate-issued CRL revokes intermediates's serial number. This is
+  // irrelevant.
+  leaf->SetCrlDistributionPointUrl(
+      CreateAndServeCrl(intermediate.get(), {intermediate->GetSerialNumber()}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_MAC && IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else {
+    // Should pass, leaf and intermediate were covered by CRLs and were not
+    // revoked.
+    EXPECT_THAT(error, IsOk());
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationSoftFailLeafRevokedByCrl) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Root-issued CRL which does not revoke intermediate.
+  intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {}));
+
+  // Leaf is revoked by intermediate issued CRL.
+  leaf->SetCrlDistributionPointUrl(
+      CreateAndServeCrl(intermediate.get(), {leaf->GetSerialNumber()}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) {
+    // TODO(mattm): CertVerifyProcBuiltin CRL handling.
+    EXPECT_THAT(error, IsOk());
+  } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC &&
+             IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else {
+    // Should fail, leaf is revoked.
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationSoftFailIntermediateRevokedByCrl) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Intermediate is revoked by root issued CRL.
+  intermediate->SetCrlDistributionPointUrl(
+      CreateAndServeCrl(root.get(), {intermediate->GetSerialNumber()}));
+
+  // Intermediate-issued CRL which does not revoke leaf.
+  leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) {
+    // TODO(mattm): CertVerifyProcBuiltin CRL handling.
+    EXPECT_THAT(error, IsOk());
+  } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC &&
+             IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else {
+    // Should fail, intermediate is revoked.
+    EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+// CRL soft fail test where the intermediate certificate has a good CRL, but
+// the leaf's distribution point returns an http error. Verification should
+// succeed.
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationSoftFailLeafCrlDpHttpError) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Serve a root-issued CRL which does not revoke intermediate.
+  intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {}));
+
+  // Serve a 404 for the intermediate-issued CRL distribution point url.
+  leaf->SetCrlDistributionPointUrl(RegisterSimpleTestServerHandler(
+      MakeRandomPath(".crl"), HTTP_NOT_FOUND, "text/plain", "Not Found"));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_WIN) {
+    // Win gives ERR_CERT_UNABLE_TO_CHECK_REVOCATION if the revocation checking
+    // network requests failed. TODO(mattm): should we ignore that?
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC &&
+             IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else {
+    // Should succeed due to soft-fail revocation checking.
+    EXPECT_THAT(error, IsOk());
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
+// CRL soft fail test where the leaf certificate has a good CRL, but
+// the intermediate's distribution point returns an http error. Verification
+// should succeed.
+TEST_P(CertVerifyProcInternalWithNetFetchingTest,
+       RevocationSoftFailIntermediateCrlDpHttpError) {
+  if (!SupportsSoftFailRevChecking()) {
+    LOG(INFO) << "Skipping test as verifier doesn't support "
+                 "VERIFY_REV_CHECKING_ENABLED";
+    return;
+  }
+
+  const char kHostname[] = "www.example.com";
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateSimpleCertBuilderChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Serve a 404 for the root-issued CRL distribution point url.
+  intermediate->SetCrlDistributionPointUrl(RegisterSimpleTestServerHandler(
+      MakeRandomPath(".crl"), HTTP_NOT_FOUND, "text/plain", "Not Found"));
+
+  // Serve an intermediate-issued CRL which does not revoke leaf.
+  leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {}));
+
+  // Trust the root and build a chain to verify that includes the intermediate.
+  ScopedTestRoot scoped_root(root->GetX509Certificate().get());
+  scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate(
+      leaf->DupCertBuffer(), intermediate->DupCertBuffer());
+  ASSERT_TRUE(chain.get());
+
+  // Verify with soft-fail revocation checking.
+  const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
+  CertVerifyResult verify_result;
+  int error =
+      Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
+
+  if (verify_proc_type() == CERT_VERIFY_PROC_WIN) {
+    // Win gives ERR_CERT_UNABLE_TO_CHECK_REVOCATION if the revocation checking
+    // network requests failed. TODO(mattm): should we ignore that?
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC &&
+             IsMacAtLeastOS10_12()) {
+    // CRL handling seems broken on macOS >= 10.12.
+    // TODO(mattm): followup on this.
+    EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION));
+  } else {
+    // Should succeed due to soft-fail revocation checking.
+    EXPECT_THAT(error, IsOk());
+  }
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+
 TEST(CertVerifyProcTest, RejectsMD2) {
   scoped_refptr<X509Certificate> cert(
       ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
diff --git a/net/cert/crl_set_fuzzer.cc b/net/cert/crl_set_fuzzer.cc
new file mode 100644
index 0000000..97d029c
--- /dev/null
+++ b/net/cert/crl_set_fuzzer.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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 <stddef.h>
+#include <stdint.h>
+
+#include "base/test/fuzzed_data_provider.h"
+#include "net/cert/crl_set.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  base::FuzzedDataProvider data_provider(data, size);
+  const std::string str = data_provider.ConsumeRandomLengthString(size);
+  scoped_refptr<net::CRLSet> out_crl_set;
+  net::CRLSet::Parse(str, &out_crl_set);
+  return 0;
+}
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_intermediate_serial.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_intermediate_serial.raw
new file mode 100644
index 0000000..370d488
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_intermediate_serial.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_spki.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_spki.raw
new file mode 100644
index 0000000..acc4c15
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_spki.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_subject_no_spki.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_subject_no_spki.raw
new file mode 100644
index 0000000..3a22aa7d
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_subject_no_spki.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_serial.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_serial.raw
new file mode 100644
index 0000000..31f6b462
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_serial.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject.raw
new file mode 100644
index 0000000..95404126
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject_no_spki.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject_no_spki.raw
new file mode 100644
index 0000000..bc10681c
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject_no_spki.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-C.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-C.raw
new file mode 100644
index 0000000..c8ff83b
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-C.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-CD-and-FE.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-CD-and-FE.raw
new file mode 100644
index 0000000..4b04356
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-CD-and-FE.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-D-and-E.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-D-and-E.raw
new file mode 100644
index 0000000..eacdb4aaf
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-D-and-E.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-E.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-E.raw
new file mode 100644
index 0000000..02051b8f
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-E.raw
Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-unrelated.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-unrelated.raw
new file mode 100644
index 0000000..83fd62c
--- /dev/null
+++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-unrelated.raw
Binary files differ
diff --git a/net/data/ocsp_unittest/make_ocsp.py b/net/data/ocsp_unittest/make_ocsp.py
index abbc5a47..4c5032e 100755
--- a/net/data/ocsp_unittest/make_ocsp.py
+++ b/net/data/ocsp_unittest/make_ocsp.py
@@ -289,8 +289,6 @@
 
   ocsp_request_der = CreateOCSPRequestDer(ca_cert_pem, cert_pem)
 
-  d64 = base64.b64encode(encoder.encode(data))
-  wd64 = '\n'.join(d64[pos:pos + 64] for pos in xrange(0, len(d64), 64))
   out = ('%s\n%s\n%s\n\n%s\n%s') % (
       description,
       MakePemBlock(encoder.encode(data), "OCSP RESPONSE"),
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README
index 32c7d287..9f9714f6 100644
--- a/net/data/ssl/certificates/README
+++ b/net/data/ssl/certificates/README
@@ -95,10 +95,6 @@
 
 - unittest.key.bin : private key stored unencrypted.
 
-- unittest.originbound.der: A test origin-bound certificate for
-     https://www.google.com:443.
-- unittest.originbound.key.der: matching PrivateKeyInfo.
-
 - multivalue_rdn.pem : A regression test for http://crbug.com/101009. A
      certificate with all of the AttributeTypeAndValues stored within a single
      RelativeDistinguishedName, rather than one AVA per RDN as normally seen.
diff --git a/net/data/ssl/certificates/unittest.originbound.der b/net/data/ssl/certificates/unittest.originbound.der
deleted file mode 100644
index b37f593..0000000
--- a/net/data/ssl/certificates/unittest.originbound.der
+++ /dev/null
Binary files differ
diff --git a/net/data/ssl/certificates/unittest.originbound.key.der b/net/data/ssl/certificates/unittest.originbound.key.der
deleted file mode 100644
index 1ecdd1eb..0000000
--- a/net/data/ssl/certificates/unittest.originbound.key.der
+++ /dev/null
Binary files differ
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc
index c9b32bbb..e8eab5d2 100644
--- a/net/disk_cache/simple/simple_backend_impl.cc
+++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -23,12 +23,12 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool/thread_pool.h"
 #include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "base/trace_event/process_memory_dump.h"
@@ -286,7 +286,7 @@
       base::MakeRefCounted<net::PrioritizedTaskRunner>(worker_pool);
 
   index_ = std::make_unique<SimpleIndex>(
-      base::ThreadTaskRunnerHandle::Get(), cleanup_tracker_.get(), this,
+      base::SequencedTaskRunnerHandle::Get(), cleanup_tracker_.get(), this,
       GetCacheType(),
       std::make_unique<SimpleIndexFile>(cache_runner_, worker_pool.get(),
                                         GetCacheType(), path_));
diff --git a/net/disk_cache/simple/simple_backend_impl.h b/net/disk_cache/simple/simple_backend_impl.h
index 4237f85c..ccf994340 100644
--- a/net/disk_cache/simple/simple_backend_impl.h
+++ b/net/disk_cache/simple/simple_backend_impl.h
@@ -41,15 +41,17 @@
 
 // SimpleBackendImpl is a new cache backend that stores entries in individual
 // files.
-// See http://www.chromium.org/developers/design-documents/network-stack/disk-cache/very-simple-backend
+// See
+// http://www.chromium.org/developers/design-documents/network-stack/disk-cache/very-simple-backend
 //
 // The SimpleBackendImpl provides safe iteration; mutating entries during
 // iteration cannot cause a crash. It is undefined whether entries created or
 // destroyed during the iteration will be included in any pre-existing
 // iterations.
 //
-// The non-static functions below must be called on the IO thread unless
-// otherwise stated.
+// The non-static functions below must be called on the source creation sequence
+// unless otherwise stated.  Historically the source creation sequence has been
+// the IO thread, but the simple backend may now be used from other sequences.
 
 class BackendCleanupTracker;
 class SimpleEntryImpl;
@@ -204,8 +206,8 @@
                                            Int64CompletionOnceCallback callback,
                                            int result);
 
-  // Try to create the directory if it doesn't exist. This must run on the IO
-  // thread.
+  // Try to create the directory if it doesn't exist. This must run on the
+  // source creation sequence.
   static DiskStatResult InitCacheStructureOnDisk(const base::FilePath& path,
                                                  uint64_t suggested_max_size,
                                                  net::CacheType cache_type);
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc
index 4828fcf..2a4754f 100644
--- a/net/disk_cache/simple/simple_entry_impl.cc
+++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -15,11 +15,10 @@
 #include "base/callback.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/task_runner.h"
 #include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "net/base/io_buffer.h"
@@ -125,7 +124,7 @@
                            net::CompletionOnceCallback callback,
                            int rv) {
   if (!sync_possible && !callback.is_null()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback), rv));
     return net::ERR_IO_PENDING;
   } else {
@@ -383,7 +382,7 @@
 }
 
 void SimpleEntryImpl::Close() {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_LT(0, open_count_);
 
   net_log_.AddEvent(net::NetLogEventType::SIMPLE_CACHE_ENTRY_CLOSE_CALL);
@@ -401,23 +400,23 @@
 }
 
 std::string SimpleEntryImpl::GetKey() const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return key_;
 }
 
 Time SimpleEntryImpl::GetLastUsed() const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(cache_type_ != net::APP_CACHE);
   return last_used_;
 }
 
 Time SimpleEntryImpl::GetLastModified() const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return last_modified_;
 }
 
 int32_t SimpleEntryImpl::GetDataSize(int stream_index) const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_LE(0, data_size_[stream_index]);
   return data_size_[stream_index];
 }
@@ -427,7 +426,7 @@
                               net::IOBuffer* buf,
                               int buf_len,
                               CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (net_log_.IsCapturing()) {
     net_log_.AddEvent(net::NetLogEventType::SIMPLE_CACHE_ENTRY_READ_CALL,
@@ -471,7 +470,7 @@
                                int buf_len,
                                CompletionOnceCallback callback,
                                bool truncate) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (net_log_.IsCapturing()) {
     net_log_.AddEvent(net::NetLogEventType::SIMPLE_CACHE_ENTRY_WRITE_CALL,
@@ -551,7 +550,7 @@
                                     net::IOBuffer* buf,
                                     int buf_len,
                                     CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (net_log_.IsCapturing()) {
     net_log_.AddEvent(net::NetLogEventType::SIMPLE_CACHE_ENTRY_READ_SPARSE_CALL,
@@ -577,7 +576,7 @@
                                      net::IOBuffer* buf,
                                      int buf_len,
                                      CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (net_log_.IsCapturing()) {
     net_log_.AddEvent(
@@ -604,7 +603,7 @@
                                        int len,
                                        int64_t* start,
                                        CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (offset < 0 || len < 0)
     return net::ERR_INVALID_ARGUMENT;
 
@@ -615,20 +614,20 @@
 }
 
 bool SimpleEntryImpl::CouldBeSparse() const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO(morlovich): Actually check.
   return true;
 }
 
 void SimpleEntryImpl::CancelSparseIO() {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // The Simple Cache does not return distinct objects for the same non-doomed
   // entry, so there's no need to coordinate which object is performing sparse
   // I/O.  Therefore, CancelSparseIO and ReadyForSparseIO succeed instantly.
 }
 
 net::Error SimpleEntryImpl::ReadyForSparseIO(CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // The simple Cache does not return distinct objects for the same non-doomed
   // entry, so there's no need to coordinate which object is performing sparse
   // I/O.  Therefore, CancelSparseIO and ReadyForSparseIO succeed instantly.
@@ -654,7 +653,7 @@
 }
 
 SimpleEntryImpl::~SimpleEntryImpl() {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(0U, pending_operations_.size());
 
   // STATE_IO_PENDING is possible here in one corner case: the entry had
@@ -675,7 +674,7 @@
     return;
   // Note that the callback is posted rather than directly invoked to avoid
   // reentrancy issues.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&InvokeCallbackIfBackendIsAlive, backend_,
                                 std::move(callback), result));
 }
@@ -724,7 +723,7 @@
   // potentially call ReturnEntryToCaller and then roll it back rather than
   // delay ReturnEntryToCaller: it protects |this| till any potential ownership
   // share transfer is sorted out.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&InvokeCallbackIfBackendIsAliveOrCloseEntry, backend_,
                      base::Unretained(this), std::move(callback)));
@@ -740,7 +739,7 @@
 }
 
 void SimpleEntryImpl::RunNextOperationIfNeeded() {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!pending_operations_.empty() && state_ != STATE_IO_PENDING) {
     SimpleEntryOperation operation = std::move(pending_operations_.front());
     pending_operations_.pop();
@@ -953,7 +952,7 @@
 }
 
 void SimpleEntryImpl::CloseInternal() {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (open_count_ != 0) {
     // Entry got resurrected in between Close and CloseInternal, nothing to do
@@ -1017,7 +1016,7 @@
                                       net::IOBuffer* buf,
                                       int buf_len,
                                       net::CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ScopedOperationRunner operation_runner(this);
 
   if (net_log_.IsCapturing()) {
@@ -1111,7 +1110,7 @@
                                         int buf_len,
                                         net::CompletionOnceCallback callback,
                                         bool truncate) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ScopedOperationRunner operation_runner(this);
 
   if (net_log_.IsCapturing()) {
@@ -1127,7 +1126,7 @@
                         CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED));
     }
     if (!callback.is_null()) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, base::BindOnce(std::move(callback), net::ERR_FAILED));
     }
     // |this| may be destroyed after return here.
@@ -1140,7 +1139,7 @@
   if (stream_index == 0) {
     int ret_value = SetStream0Data(buf, offset, buf_len, truncate);
     if (!callback.is_null()) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, base::BindOnce(std::move(callback), ret_value));
     }
     return;
@@ -1153,7 +1152,7 @@
       RecordWriteResult(cache_type_,
                         SIMPLE_ENTRY_WRITE_RESULT_FAST_EMPTY_RETURN);
       if (!callback.is_null()) {
-        base::ThreadTaskRunnerHandle::Get()->PostTask(
+        base::SequencedTaskRunnerHandle::Get()->PostTask(
             FROM_HERE, base::BindOnce(std::move(callback), 0));
       }
       return;
@@ -1229,7 +1228,7 @@
     net::IOBuffer* buf,
     int buf_len,
     net::CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ScopedOperationRunner operation_runner(this);
 
   if (net_log_.IsCapturing()) {
@@ -1245,7 +1244,7 @@
           CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED));
     }
     if (!callback.is_null()) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, base::BindOnce(std::move(callback), net::ERR_FAILED));
     }
     // |this| may be destroyed after return here.
@@ -1274,7 +1273,7 @@
     net::IOBuffer* buf,
     int buf_len,
     net::CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ScopedOperationRunner operation_runner(this);
 
   if (net_log_.IsCapturing()) {
@@ -1290,7 +1289,7 @@
           CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED));
     }
     if (!callback.is_null()) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, base::BindOnce(std::move(callback), net::ERR_FAILED));
     }
     // |this| may be destroyed after return here.
@@ -1330,12 +1329,12 @@
     int len,
     int64_t* out_start,
     net::CompletionOnceCallback callback) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ScopedOperationRunner operation_runner(this);
 
   if (state_ == STATE_FAILURE || state_ == STATE_UNINITIALIZED) {
     if (!callback.is_null()) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, base::BindOnce(std::move(callback), net::ERR_FAILED));
     }
     // |this| may be destroyed after return here.
@@ -1424,7 +1423,7 @@
     Entry** out_entry,
     bool* out_opened,
     net::NetLogEventType end_event_type) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, STATE_IO_PENDING);
   DCHECK(in_results);
   ScopedOperationRunner operation_runner(this);
@@ -1528,7 +1527,7 @@
     net::CompletionOnceCallback completion_callback,
     const SimpleEntryStat& entry_stat,
     int result) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(synchronous_entry_);
   DCHECK_EQ(STATE_IO_PENDING, state_);
   if (result < 0) {
@@ -1540,7 +1539,7 @@
   }
 
   if (!completion_callback.is_null()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(completion_callback), result));
   }
   RunNextOperationIfNeeded();
@@ -1552,7 +1551,7 @@
     net::CompletionOnceCallback completion_callback,
     std::unique_ptr<SimpleEntryStat> entry_stat,
     std::unique_ptr<SimpleSynchronousEntry::ReadResult> read_result) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(synchronous_entry_);
   DCHECK_EQ(STATE_IO_PENDING, state_);
   DCHECK(read_result);
@@ -1623,7 +1622,7 @@
     net::CompletionOnceCallback completion_callback,
     std::unique_ptr<base::Time> last_used,
     std::unique_ptr<int> result) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(synchronous_entry_);
   DCHECK(result);
 
@@ -1641,7 +1640,7 @@
     net::CompletionOnceCallback completion_callback,
     std::unique_ptr<SimpleEntryStat> entry_stat,
     std::unique_ptr<int> result) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(synchronous_entry_);
   DCHECK(result);
 
@@ -1656,7 +1655,7 @@
 void SimpleEntryImpl::GetAvailableRangeOperationComplete(
     net::CompletionOnceCallback completion_callback,
     std::unique_ptr<int> result) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(synchronous_entry_);
   DCHECK(result);
 
@@ -1681,7 +1680,7 @@
 void SimpleEntryImpl::RecordReadResultConsideringChecksum(
     const std::unique_ptr<SimpleSynchronousEntry::ReadResult>& read_result)
     const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(synchronous_entry_);
   DCHECK_EQ(STATE_IO_PENDING, state_);
 
@@ -1716,7 +1715,7 @@
 
 void SimpleEntryImpl::UpdateDataFromEntryStat(
     const SimpleEntryStat& entry_stat) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(synchronous_entry_);
   DCHECK_EQ(STATE_READY, state_);
 
@@ -1787,7 +1786,7 @@
   base::Time modification_time = base::Time::Now();
 
   // Reset checksum; SimpleSynchronousEntry::Close will compute it for us,
-  // and do it off the I/O thread.
+  // and do it off the source creation sequence.
   crc32s_end_offset_[0] = 0;
 
   UpdateDataFromEntryStat(
diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h
index dbf6098..cae5d5d 100644
--- a/net/disk_cache/simple/simple_entry_impl.h
+++ b/net/disk_cache/simple/simple_entry_impl.h
@@ -13,7 +13,7 @@
 #include "base/containers/queue.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
 #include "net/base/cache_type.h"
 #include "net/base/net_export.h"
 #include "net/base/request_priority.h"
@@ -44,9 +44,9 @@
 class SimpleSynchronousEntry;
 struct SimpleEntryCreationResults;
 
-// SimpleEntryImpl is the IO thread interface to an entry in the very simple
-// disk cache. It proxies for the SimpleSynchronousEntry, which performs IO
-// on the worker thread.
+// SimpleEntryImpl is the source task_runner interface to an entry in the very
+// simple disk cache. It proxies for the SimpleSynchronousEntry, which performs
+// IO on the worker thread.
 class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
     public base::RefCounted<SimpleEntryImpl> {
   friend class base::RefCounted<SimpleEntryImpl>;
@@ -307,7 +307,7 @@
 
   // Called after an asynchronous write completes.
   // |buf| parameter brings back a reference to net::IOBuffer to the original
-  // thread, so that we can reduce cross thread malloc/free pair.
+  // sequence, so that we can reduce cross thread malloc/free pair.
   // See http://crbug.com/708644 for details.
   void WriteOperationComplete(
       int stream_index,
@@ -367,9 +367,10 @@
 
   std::unique_ptr<ActiveEntryProxy> active_entry_proxy_;
 
-  // All nonstatic SimpleEntryImpl methods should always be called on the IO
-  // thread, in all cases. |io_thread_checker_| documents and enforces this.
-  base::ThreadChecker io_thread_checker_;
+  // All nonstatic SimpleEntryImpl methods should always be called on the
+  // source creation sequence, in all cases. |sequence_checker_| documents and
+  // enforces this.
+  SEQUENCE_CHECKER(sequence_checker_);
 
   const base::WeakPtr<SimpleBackendImpl> backend_;
   SimpleFileTracker* const file_tracker_;
diff --git a/net/disk_cache/simple/simple_index.cc b/net/disk_cache/simple/simple_index.cc
index cd12bb20..a4ee6f7 100644
--- a/net/disk_cache/simple/simple_index.cc
+++ b/net/disk_cache/simple/simple_index.cc
@@ -177,7 +177,7 @@
 }
 
 SimpleIndex::SimpleIndex(
-    const scoped_refptr<base::SingleThreadTaskRunner>& io_thread,
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
     scoped_refptr<BackendCleanupTracker> cleanup_tracker,
     SimpleIndexDelegate* delegate,
     net::CacheType cache_type,
@@ -186,7 +186,7 @@
       delegate_(delegate),
       cache_type_(cache_type),
       index_file_(std::move(index_file)),
-      io_thread_(io_thread),
+      task_runner_(task_runner),
       // Creating the callback once so it is reused every time
       // write_to_disk_timer_.Start() is called.
       write_to_disk_cb_(base::Bind(&SimpleIndex::WriteToDisk,
@@ -194,7 +194,7 @@
                                    INDEX_WRITE_REASON_IDLE)) {}
 
 SimpleIndex::~SimpleIndex() {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Fail all callbacks waiting for the index to come up.
   for (auto it = to_run_when_initialized_.begin(),
@@ -205,7 +205,7 @@
 }
 
 void SimpleIndex::Initialize(base::Time cache_mtime) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
 #if defined(OS_ANDROID)
   if (app_status_listener_) {
@@ -238,9 +238,9 @@
 }
 
 net::Error SimpleIndex::ExecuteWhenReady(net::CompletionOnceCallback task) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (initialized_)
-    io_thread_->PostTask(FROM_HERE, base::BindOnce(std::move(task), net::OK));
+    task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(task), net::OK));
   else
     to_run_when_initialized_.push_back(std::move(task));
   return net::ERR_IO_PENDING;
@@ -316,7 +316,7 @@
 }
 
 base::Time SimpleIndex::GetLastUsedTime(uint64_t entry_hash) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_NE(cache_type_, net::APP_CACHE);
   auto it = entries_set_.find(entry_hash);
   if (it == entries_set_.end())
@@ -336,7 +336,7 @@
 }
 
 void SimpleIndex::Insert(uint64_t entry_hash) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Upon insert we don't know yet the size of the entry.
   // It will be updated later when the SimpleEntryImpl finishes opening or
   // creating the new entry, and then UpdateEntrySize will be called.
@@ -355,7 +355,7 @@
 }
 
 void SimpleIndex::Remove(uint64_t entry_hash) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   bool need_write = false;
   auto it = entries_set_.find(entry_hash);
   if (it != entries_set_.end()) {
@@ -372,13 +372,13 @@
 }
 
 bool SimpleIndex::Has(uint64_t hash) const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // If not initialized, always return true, forcing it to go to the disk.
   return !initialized_ || entries_set_.count(hash) > 0;
 }
 
 uint8_t SimpleIndex::GetEntryInMemoryData(uint64_t entry_hash) const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = entries_set_.find(entry_hash);
   if (it == entries_set_.end())
     return 0;
@@ -386,7 +386,7 @@
 }
 
 void SimpleIndex::SetEntryInMemoryData(uint64_t entry_hash, uint8_t value) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = entries_set_.find(entry_hash);
   if (it == entries_set_.end())
     return;
@@ -394,7 +394,7 @@
 }
 
 bool SimpleIndex::UseIfExists(uint64_t entry_hash) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Always update the last used time, even if it is during initialization.
   // It will be merged later.
   auto it = entries_set_.find(entry_hash);
@@ -410,7 +410,7 @@
 }
 
 void SimpleIndex::StartEvictionIfNeeded() {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (eviction_in_progress_ || cache_size_ <= high_watermark_)
     return;
   // Take all live key hashes from the index and sort them by time.
@@ -467,7 +467,7 @@
 }
 
 int32_t SimpleIndex::GetTrailerPrefetchSize(uint64_t entry_hash) const {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(cache_type_, net::APP_CACHE);
   auto it = entries_set_.find(entry_hash);
   if (it == entries_set_.end())
@@ -476,7 +476,7 @@
 }
 
 void SimpleIndex::SetTrailerPrefetchSize(uint64_t entry_hash, int32_t size) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(cache_type_, net::APP_CACHE);
   auto it = entries_set_.find(entry_hash);
   if (it == entries_set_.end())
@@ -489,7 +489,7 @@
 
 bool SimpleIndex::UpdateEntrySize(uint64_t entry_hash,
                                   base::StrictNumeric<uint32_t> entry_size) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto it = entries_set_.find(entry_hash);
   if (it == entries_set_.end())
     return false;
@@ -505,7 +505,7 @@
 }
 
 void SimpleIndex::EvictionDone(int result) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Ignore the result of eviction. We did our best.
   eviction_in_progress_ = false;
@@ -549,7 +549,7 @@
     EntrySet::iterator* it,
     base::StrictNumeric<uint32_t> entry_size) {
   // Update the total cache size with the new entry size.
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_GE(cache_size_, (*it)->second.GetEntrySize());
   uint32_t original_size = (*it)->second.GetEntrySize();
   cache_size_ -= (*it)->second.GetEntrySize();
@@ -563,7 +563,7 @@
 
 void SimpleIndex::MergeInitializingSet(
     std::unique_ptr<SimpleIndexLoadResult> load_result) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   EntrySet* index_file_entries = &load_result->entries;
 
@@ -619,7 +619,7 @@
   for (auto it = to_run_when_initialized_.begin(),
             end = to_run_when_initialized_.end();
        it != end; ++it) {
-    io_thread_->PostTask(FROM_HERE, base::BindOnce(std::move(*it), net::OK));
+    task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(*it), net::OK));
   }
   to_run_when_initialized_.clear();
 }
@@ -627,7 +627,7 @@
 #if defined(OS_ANDROID)
 void SimpleIndex::OnApplicationStateChange(
     base::android::ApplicationState state) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // For more info about android activities, see:
   // developer.android.com/training/basics/activity-lifecycle/pausing.html
   if (state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) {
@@ -641,7 +641,7 @@
 #endif
 
 void SimpleIndex::WriteToDisk(IndexWriteToDiskReason reason) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!initialized_)
     return;
 
diff --git a/net/disk_cache/simple/simple_index.h b/net/disk_cache/simple/simple_index.h
index c3e3e02..f021cf1 100644
--- a/net/disk_cache/simple/simple_index.h
+++ b/net/disk_cache/simple/simple_index.h
@@ -20,8 +20,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/numerics/safe_conversions.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
@@ -134,7 +134,7 @@
 
   typedef std::vector<uint64_t> HashList;
 
-  SimpleIndex(const scoped_refptr<base::SingleThreadTaskRunner>& io_thread,
+  SimpleIndex(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
               scoped_refptr<BackendCleanupTracker> cleanup_tracker,
               SimpleIndexDelegate* delegate,
               net::CacheType cache_type,
@@ -289,11 +289,12 @@
 
   std::unique_ptr<SimpleIndexFile> index_file_;
 
-  scoped_refptr<base::SingleThreadTaskRunner> io_thread_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
-  // All nonstatic SimpleEntryImpl methods should always be called on the IO
-  // thread, in all cases. |io_thread_checker_| documents and enforces this.
-  base::ThreadChecker io_thread_checker_;
+  // All nonstatic SimpleEntryImpl methods should always be called on its
+  // creation sequance, in all cases. |sequence_checker_| documents and
+  // enforces this.
+  SEQUENCE_CHECKER(sequence_checker_);
 
   // Timestamp of the last time we wrote the index to disk.
   // PostponeWritingToDisk() may give up postponing and allow the write if it
diff --git a/net/disk_cache/simple/simple_index_file.h b/net/disk_cache/simple/simple_index_file.h
index b8272fb5..14e2da4 100644
--- a/net/disk_cache/simple/simple_index_file.h
+++ b/net/disk_cache/simple/simple_index_file.h
@@ -48,7 +48,7 @@
 // the format see |SimpleIndexFile::Serialize()| and
 // |SimpleIndexFile::LoadFromDisk()|.
 //
-// The non-static methods must run on the IO thread. All the real
+// The non-static methods must run on the source creation sequence. All the real
 // work is done in the static methods, which are run on the cache thread
 // or in worker threads. Synchronization between methods is the
 // responsibility of the caller.
@@ -121,7 +121,8 @@
                                                 int64_t size)>;
 
   // When loading the entries from disk, add this many extra hash buckets to
-  // prevent reallocation on the IO thread when merging in new live entries.
+  // prevent reallocation on the creation sequence when merging in new live
+  // entries.
   static const int kExtraSizeForMerge = 512;
 
   // Synchronous (IO performing) implementation of LoadIndexEntries.
diff --git a/services/device/wake_lock/power_save_blocker/OWNERS b/services/device/wake_lock/power_save_blocker/OWNERS
index 2fddade4..fdd793c 100644
--- a/services/device/wake_lock/power_save_blocker/OWNERS
+++ b/services/device/wake_lock/power_save_blocker/OWNERS
@@ -1,5 +1,4 @@
 boliu@chromium.org
 hashimoto@chromium.org
 
-per-file power_save_blocker_chromeos.cc=derat@chromium.org
 # COMPONENT: Internals>Core
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc
index 12bd2ad..7bdf6ab 100644
--- a/services/identity/public/cpp/identity_manager.cc
+++ b/services/identity/public/cpp/identity_manager.cc
@@ -8,7 +8,6 @@
 
 #include "build/build_config.h"
 #include "components/signin/core/browser/account_fetcher_service.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/ubertoken_fetcher_impl.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "services/identity/public/cpp/accounts_cookie_mutator.h"
@@ -66,15 +65,7 @@
   token_service_->AddDiagnosticsObserver(this);
   token_service_->AddObserver(this);
   account_tracker_service_->AddObserver(this);
-
-  // IdentityManager owns gaia_cookie_manager_service_ and will outlive it, so
-  // base::Unretained is safe.
-  gaia_cookie_manager_service_->SetGaiaAccountsInCookieUpdatedCallback(
-      base::BindRepeating(&IdentityManager::OnGaiaAccountsInCookieUpdated,
-                          base::Unretained(this)));
-  gaia_cookie_manager_service_->SetGaiaCookieDeletedByUserActionCallback(
-      base::BindRepeating(&IdentityManager::OnGaiaCookieDeletedByUserAction,
-                          base::Unretained(this)));
+  gaia_cookie_manager_service_->AddObserver(this);
 
   // Seed the primary account with any state that |signin_manager_| loaded from
   // prefs.
@@ -95,6 +86,7 @@
   token_service_->RemoveObserver(this);
   token_service_->RemoveDiagnosticsObserver(this);
   account_tracker_service_->RemoveObserver(this);
+  gaia_cookie_manager_service_->RemoveObserver(this);
 }
 
 // TODO(862619) change return type to base::Optional<CoreAccountInfo>
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h
index 1fe4c55..7f575e6 100644
--- a/services/identity/public/cpp/identity_manager.h
+++ b/services/identity/public/cpp/identity_manager.h
@@ -13,6 +13,7 @@
 #include "components/signin/core/browser/account_fetcher_service.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_manager_base.h"
 #include "components/signin/core/browser/signin_metrics.h"
@@ -35,8 +36,6 @@
 class PrefRegistrySimple;
 class SigninManagerAndroid;
 
-class GaiaCookieManagerService;
-
 namespace identity {
 
 class AccountsMutator;
@@ -53,6 +52,7 @@
 class IdentityManager : public SigninManagerBase::Observer,
                         public OAuth2TokenService::DiagnosticsObserver,
                         public OAuth2TokenService::Observer,
+                        public GaiaCookieManagerService::Observer,
                         public AccountTrackerService::Observer {
  public:
   class Observer {
@@ -596,12 +596,12 @@
   void OnAuthErrorChanged(const std::string& account_id,
                           const GoogleServiceAuthError& auth_error) override;
 
-  // GaiaCookieManagerService callbacks:
+  // GaiaCookieManagerService::Observer:
   void OnGaiaAccountsInCookieUpdated(
       const std::vector<gaia::ListedAccount>& signed_in_accounts,
       const std::vector<gaia::ListedAccount>& signed_out_accounts,
-      const GoogleServiceAuthError& error);
-  void OnGaiaCookieDeletedByUserAction();
+      const GoogleServiceAuthError& error) override;
+  void OnGaiaCookieDeletedByUserAction() override;
 
   // OAuth2TokenService::DiagnosticsObserver:
   void OnAccessTokenRequested(
diff --git a/services/identity/public/cpp/identity_test_utils.cc b/services/identity/public/cpp/identity_test_utils.cc
index 5e94362..641f11e 100644
--- a/services/identity/public/cpp/identity_test_utils.cc
+++ b/services/identity/public/cpp/identity_test_utils.cc
@@ -10,7 +10,6 @@
 #include "base/run_loop.h"
 #include "base/strings/string_split.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/list_accounts_test_utils.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "google_apis/gaia/gaia_auth_util.h"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 54b4ee5..57a9145 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -3460,6 +3460,13 @@
       "libANGLE"
     ]
   },
+  "android-code-coverage": {
+    "junit_tests": [
+      {
+        "test": "base_junit_tests"
+      }
+    ]
+  },
   "android-mojo-webview-rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index c4db86f..e62cbb2 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -33,7 +33,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 3
         },
         "trigger_script": {
@@ -77,7 +77,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         },
         "trigger_script": {
           "args": [
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 8fef580d3..7cd5178 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -30,7 +30,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -69,7 +69,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -108,7 +108,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -151,7 +151,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 16
         },
         "trigger_script": {
@@ -198,7 +198,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 16
         },
         "trigger_script": {
@@ -245,7 +245,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 12
         },
         "trigger_script": {
@@ -286,7 +286,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -323,7 +323,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -360,7 +360,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -399,7 +399,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -438,7 +438,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -479,7 +479,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 5
         },
         "trigger_script": {
@@ -520,7 +520,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -557,7 +557,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -594,7 +594,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -634,7 +634,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 5
         },
         "trigger_script": {
@@ -676,7 +676,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         }
       },
       {
@@ -700,7 +700,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         }
       },
       {
@@ -724,7 +724,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         }
       },
       {
@@ -748,7 +748,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         }
       }
     ]
@@ -785,7 +785,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 19
         },
         "trigger_script": {
@@ -831,7 +831,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 25
         },
         "trigger_script": {
@@ -875,7 +875,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -914,7 +914,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -953,7 +953,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -992,7 +992,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1031,7 +1031,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1070,7 +1070,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1113,7 +1113,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 16
         },
         "trigger_script": {
@@ -1159,7 +1159,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 35
         },
         "trigger_script": {
@@ -1205,7 +1205,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 28
         },
         "trigger_script": {
@@ -1247,7 +1247,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         }
       },
       {
@@ -1271,7 +1271,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         }
       },
       {
@@ -1295,7 +1295,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         }
       },
       {
@@ -1319,7 +1319,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800
+          "io_timeout": 14400
         }
       }
     ]
@@ -1356,7 +1356,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1393,7 +1393,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1430,7 +1430,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1467,7 +1467,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1504,7 +1504,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1541,7 +1541,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1582,7 +1582,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 26
         },
         "trigger_script": {
@@ -1623,7 +1623,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1660,7 +1660,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1701,7 +1701,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 26
         },
         "trigger_script": {
@@ -1742,7 +1742,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1779,7 +1779,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1816,7 +1816,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1853,7 +1853,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1890,7 +1890,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -1931,7 +1931,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 26
         },
         "trigger_script": {
@@ -1978,7 +1978,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -2015,7 +2015,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -2052,7 +2052,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -2089,7 +2089,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -2126,7 +2126,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 1
         },
         "trigger_script": {
@@ -2167,7 +2167,7 @@
           "expiration": 7200,
           "hard_timeout": 36000,
           "ignore_task_failure": false,
-          "io_timeout": 1800,
+          "io_timeout": 14400,
           "shards": 26
         },
         "trigger_script": {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 4f8718bb..a997b7a8 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -24,6 +24,10 @@
       'monochrome_public_test_ar_apk': {},
     },
 
+    'android_code_coverage_junit_tests': {
+      'base_junit_tests': {},
+    },
+
     'android_ddready_vr_gtests': {
       'chrome_public_test_vr_apk-ddready-cardboard': {
         'args': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9e42a9b1..4e975d1 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1598,6 +1598,14 @@
           'libANGLE',
         ],
       },
+      'android-code-coverage': {
+        'mixins': [
+          'code-coverage',
+        ],
+        'test_suites': {
+          'junit_tests': 'android_code_coverage_junit_tests',
+        }
+      },
       'android-mojo-webview-rel': {
         'swarming': {
           'dimension_sets': [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 7160184a..04a3d5546 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1874,6 +1874,24 @@
             ]
         }
     ],
+    "DownloadResumptionWithoutStrongValidators": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "download_validation_length": "1024"
+                    },
+                    "enable_features": [
+                        "AllowDownloadResumptionWithoutStrongValidators"
+                    ]
+                }
+            ]
+        }
+    ],
     "DownloadsLocationChange": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 7a4a457..f87dc3b 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -2037,6 +2037,7 @@
     "loader/frame_fetch_context_test.cc",
     "loader/frame_resource_fetcher_properties_test.cc",
     "loader/idleness_detector_test.cc",
+    "loader/image_loader_test.cc",
     "loader/interactive_detector_test.cc",
     "loader/link_loader_test.cc",
     "loader/long_task_detector_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_paint_value.cc b/third_party/blink/renderer/core/css/css_paint_value.cc
index d669c5e..e19c886 100644
--- a/third_party/blink/renderer/core/css/css_paint_value.cc
+++ b/third_party/blink/renderer/core/css/css_paint_value.cc
@@ -67,7 +67,6 @@
   // For Off-Thread PaintWorklet, we just collect the necessary inputs together
   // and defer the actual JavaScript call until much later (during cc Raster).
   if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
-
     // TODO(crbug.com/946515): Break dependency on LayoutObject.
     const LayoutObject& layout_object =
         static_cast<const LayoutObject&>(client);
diff --git a/third_party/blink/renderer/core/css/css_syntax_descriptor.h b/third_party/blink/renderer/core/css/css_syntax_descriptor.h
index ba332a8..6a819763 100644
--- a/third_party/blink/renderer/core/css/css_syntax_descriptor.h
+++ b/third_party/blink/renderer/core/css/css_syntax_descriptor.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/css/css_syntax_component.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
 
 namespace blink {
 
@@ -48,6 +49,19 @@
   Vector<CSSSyntaxComponent> syntax_components_;
 };
 
+template <wtf_size_t inlineCapacity, typename Allocator>
+struct CrossThreadCopier<
+    Vector<CSSSyntaxDescriptor, inlineCapacity, Allocator>> {
+  using Type = Vector<CSSSyntaxDescriptor, inlineCapacity, Allocator>;
+  static Type Copy(const Type& value) {
+    Type result;
+    result.ReserveInitialCapacity(value.size());
+    for (const auto& element : value)
+      result.push_back(element.IsolatedCopy());
+    return result;
+  }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_SYNTAX_DESCRIPTOR_H_
diff --git a/third_party/blink/renderer/core/css/media_values_cached.h b/third_party/blink/renderer/core/css/media_values_cached.h
index 96df16f..a9dfbd9e 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.h
+++ b/third_party/blink/renderer/core/css/media_values_cached.h
@@ -113,9 +113,7 @@
 template <>
 struct CrossThreadCopier<MediaValuesCached::MediaValuesCachedData> {
   typedef MediaValuesCached::MediaValuesCachedData Type;
-  static Type Copy(const MediaValuesCached::MediaValuesCachedData& data) {
-    return data.DeepCopy();
-  }
+  static Type Copy(const Type& data) { return data.DeepCopy(); }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
index 9278785..173a46c 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
@@ -211,8 +211,8 @@
             if (!BackgroundLayerMayBeSprite(*background_layer)) {
               if (element_->GetDocument()
                       .GetFrame()
-                      ->GetLazyLoadImageEnabledState() ==
-                  LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic) {
+                      ->GetLazyLoadImageSetting() ==
+                  LocalFrame::LazyLoadImageSetting::kEnabledAutomatic) {
                 image_request_optimization = FetchParameters::kDeferImageLoad;
               } else {
                 image_request_optimization = FetchParameters::kAllowPlaceholder;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index cee875e..d055e4a 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2218,8 +2218,8 @@
     LegacyLayout legacy = children_context.force_legacy_layout
                               ? LegacyLayout::kForce
                               : LegacyLayout::kAuto;
-    LayoutTreeBuilderForElement builder(*this, style);
-    builder.CreateLayoutObjectIfNeeded(legacy);
+    LayoutTreeBuilderForElement builder(*this, style, legacy);
+    builder.CreateLayoutObjectIfNeeded();
   }
 
   LayoutObject* layout_object = GetLayoutObject();
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.cc b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
index 8aff31dc..00707b2f 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
@@ -47,8 +47,9 @@
 
 LayoutTreeBuilderForElement::LayoutTreeBuilderForElement(
     Element& element,
-    const ComputedStyle* style)
-    : LayoutTreeBuilder(element, nullptr, style) {
+    const ComputedStyle* style,
+    LegacyLayout legacy)
+    : LayoutTreeBuilder(element, nullptr, style), legacy_(legacy) {
   DCHECK(element.CanParticipateInFlatTree());
   DCHECK(style_);
   DCHECK(!style_->IsEnsuredInDisplayNone());
@@ -56,8 +57,11 @@
   // It's an extra (unnecessary) check for text nodes, though.
   if (element.IsFirstLetterPseudoElement()) {
     if (LayoutObject* next_layout_object =
-            FirstLetterPseudoElement::FirstLetterTextLayoutObject(element))
+            FirstLetterPseudoElement::FirstLetterTextLayoutObject(element)) {
       layout_object_parent_ = next_layout_object->Parent();
+      if (layout_object_parent_->ForceLegacyLayout())
+        legacy_ = LegacyLayout::kForce;
+    }
   } else {
     layout_object_parent_ =
         LayoutTreeBuilderTraversal::ParentLayoutObject(element);
@@ -107,8 +111,8 @@
 }
 
 DISABLE_CFI_PERF
-void LayoutTreeBuilderForElement::CreateLayoutObject(LegacyLayout legacy) {
-  LayoutObject* new_layout_object = node_->CreateLayoutObject(*style_, legacy);
+void LayoutTreeBuilderForElement::CreateLayoutObject() {
+  LayoutObject* new_layout_object = node_->CreateLayoutObject(*style_, legacy_);
   if (!new_layout_object)
     return;
 
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.h b/third_party/blink/renderer/core/dom/layout_tree_builder.h
index 37a499e..201d386 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder.h
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder.h
@@ -101,18 +101,22 @@
 
 class LayoutTreeBuilderForElement : public LayoutTreeBuilder<Element> {
  public:
-  LayoutTreeBuilderForElement(Element&, const ComputedStyle*);
+  LayoutTreeBuilderForElement(Element&,
+                              const ComputedStyle*,
+                              LegacyLayout legacy);
 
-  void CreateLayoutObjectIfNeeded(LegacyLayout legacy) {
+  void CreateLayoutObjectIfNeeded() {
     if (ShouldCreateLayoutObject())
-      CreateLayoutObject(legacy);
+      CreateLayoutObject();
   }
 
  private:
   LayoutObject* ParentLayoutObject() const;
   LayoutObject* NextLayoutObject() const;
   bool ShouldCreateLayoutObject() const;
-  void CreateLayoutObject(LegacyLayout);
+  void CreateLayoutObject();
+
+  LegacyLayout legacy_;
 };
 
 class LayoutTreeBuilderForText : public LayoutTreeBuilder<Text> {
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index d1922a48..bdf2c291 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1290,23 +1290,22 @@
                          request, Client()->GetPreviewsStateForFrame());
 }
 
-LocalFrame::LazyLoadImageEnabledState LocalFrame::GetLazyLoadImageEnabledState()
-    const {
+LocalFrame::LazyLoadImageSetting LocalFrame::GetLazyLoadImageSetting() const {
   DCHECK(GetSettings());
   if (!RuntimeEnabledFeatures::LazyImageLoadingEnabled() ||
       !GetSettings()->GetLazyLoadEnabled()) {
-    return LocalFrame::LazyLoadImageEnabledState::kDisabled;
+    return LocalFrame::LazyLoadImageSetting::kDisabled;
   }
   if (!RuntimeEnabledFeatures::AutomaticLazyImageLoadingEnabled())
-    return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit;
+    return LocalFrame::LazyLoadImageSetting::kEnabledExplicit;
   if (RuntimeEnabledFeatures::
           RestrictAutomaticLazyImageLoadingToDataSaverEnabled() &&
       !is_save_data_enabled_) {
-    return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit;
+    return LocalFrame::LazyLoadImageSetting::kEnabledExplicit;
   }
   if (Owner() && !Owner()->ShouldLazyLoadChildren())
-    return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit;
-  return LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic;
+    return LocalFrame::LazyLoadImageSetting::kEnabledExplicit;
+  return LocalFrame::LazyLoadImageSetting::kEnabledAutomatic;
 }
 
 WebURLLoaderFactory* LocalFrame::GetURLLoaderFactory() {
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 784ff44b..f9d2ce6 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -317,13 +317,13 @@
   // Returns true if Client Lo-Fi should be used for this request.
   bool IsClientLoFiAllowed(const ResourceRequest&) const;
 
-  enum class LazyLoadImageEnabledState {
+  enum class LazyLoadImageSetting {
     kDisabled,
     kEnabledExplicit,
     kEnabledAutomatic
   };
   // Returns the enabled state of lazyloading of images.
-  LazyLoadImageEnabledState GetLazyLoadImageEnabledState() const;
+  LazyLoadImageSetting GetLazyLoadImageSetting() const;
 
   // The returned value is a off-heap raw-ptr and should not be stored.
   WebURLLoaderFactory* GetURLLoaderFactory();
diff --git a/third_party/blink/renderer/core/frame/local_frame_test.cc b/third_party/blink/renderer/core/frame/local_frame_test.cc
index 7bf062b..b9507054 100644
--- a/third_party/blink/renderer/core/frame/local_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_test.cc
@@ -187,8 +187,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kDisabled,
-            page_holder->GetFrame().GetLazyLoadImageEnabledState());
+  EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kDisabled,
+            page_holder->GetFrame().GetLazyLoadImageSetting());
 }
 
 TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWithSettingDisabled) {
@@ -196,8 +196,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &DisableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kDisabled,
-            page_holder->GetFrame().GetLazyLoadImageEnabledState());
+  EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kDisabled,
+            page_holder->GetFrame().GetLazyLoadImageSetting());
 }
 
 TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWithAutomaticDisabled) {
@@ -207,8 +207,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit,
-            page_holder->GetFrame().GetLazyLoadImageEnabledState());
+  EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledExplicit,
+            page_holder->GetFrame().GetLazyLoadImageSetting());
 }
 
 TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWhenNotRestricted) {
@@ -221,8 +221,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic,
-            page_holder->GetFrame().GetLazyLoadImageEnabledState());
+  EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledAutomatic,
+            page_holder->GetFrame().GetLazyLoadImageSetting());
 }
 
 TEST_F(LocalFrameTest,
@@ -236,8 +236,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit,
-            page_holder->GetFrame().GetLazyLoadImageEnabledState());
+  EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledExplicit,
+            page_holder->GetFrame().GetLazyLoadImageSetting());
 }
 
 TEST_F(LocalFrameTest,
@@ -251,8 +251,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndEnableDataSaverHoldbackInSettings);
-  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit,
-            page_holder->GetFrame().GetLazyLoadImageEnabledState());
+  EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledExplicit,
+            page_holder->GetFrame().GetLazyLoadImageSetting());
 }
 
 TEST_F(LocalFrameTest,
@@ -266,8 +266,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic,
-            page_holder->GetFrame().GetLazyLoadImageEnabledState());
+  EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledAutomatic,
+            page_holder->GetFrame().GetLazyLoadImageSetting());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index c5f40e72..7f0fba4 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -448,8 +448,8 @@
     }
   }
 
-  if (image_was_modified ||
-      GetImageLoader().ShouldUpdateOnInsertedInto(insertion_point)) {
+  if (image_was_modified || GetImageLoader().ShouldUpdateOnInsertedInto(
+                                insertion_point, referrer_policy_)) {
     GetImageLoader().UpdateFromElement(ImageLoader::kUpdateNormal,
                                        referrer_policy_);
   }
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
index 826124fb..da197da 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -306,8 +306,8 @@
         break;
       case LoadingAttrValue::kLazy:
         is_lazy_load_image_enabled =
-            document_parameters.lazy_load_image_enabled_state !=
-            LocalFrame::LazyLoadImageEnabledState::kDisabled;
+            document_parameters.lazy_load_image_setting !=
+            LocalFrame::LazyLoadImageSetting::kDisabled;
         break;
       case LoadingAttrValue::kAuto:
         if ((width_attr_dimension_type_ ==
@@ -319,8 +319,8 @@
           is_lazy_load_image_enabled = false;
         } else {
           is_lazy_load_image_enabled =
-              document_parameters.lazy_load_image_enabled_state ==
-              LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic;
+              document_parameters.lazy_load_image_setting ==
+              LocalFrame::LazyLoadImageSetting::kEnabledAutomatic;
         }
         break;
     }
@@ -1087,11 +1087,10 @@
   integrity_features = SubresourceIntegrityHelper::GetFeatures(document);
   lazyload_policy_enforced = document->IsLazyLoadPolicyEnforced();
   if (document->Loader() && document->Loader()->GetFrame()) {
-    lazy_load_image_enabled_state =
-        document->Loader()->GetFrame()->GetLazyLoadImageEnabledState();
+    lazy_load_image_setting =
+        document->Loader()->GetFrame()->GetLazyLoadImageSetting();
   } else {
-    lazy_load_image_enabled_state =
-        LocalFrame::LazyLoadImageEnabledState::kDisabled;
+    lazy_load_image_setting = LocalFrame::LazyLoadImageSetting::kDisabled;
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
index 90c3cfc8..d621de64 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
@@ -56,12 +56,6 @@
   USING_FAST_MALLOC(CachedDocumentParameters);
 
  public:
-  enum class LazyLoadImageEnabledState {
-    kDisabled,
-    kEnabledExplicit,
-    kEnabledAutomatic
-  };
-
   explicit CachedDocumentParameters(Document*);
   CachedDocumentParameters() = default;
 
@@ -72,7 +66,7 @@
   network::mojom::ReferrerPolicy referrer_policy;
   SubresourceIntegrity::IntegrityFeatures integrity_features;
   bool lazyload_policy_enforced;
-  LocalFrame::LazyLoadImageEnabledState lazy_load_image_enabled_state;
+  LocalFrame::LazyLoadImageSetting lazy_load_image_setting;
 };
 
 class TokenPreloadScanner {
diff --git a/third_party/blink/renderer/core/layout/jank_tracker.cc b/third_party/blink/renderer/core/layout/jank_tracker.cc
index 18fc48f..1fda05d 100644
--- a/third_party/blink/renderer/core/layout/jank_tracker.cc
+++ b/third_party/blink/renderer/core/layout/jank_tracker.cc
@@ -71,9 +71,8 @@
          rect.Height() * granularity_scale < 0.5;
 }
 
-static const TransformPaintPropertyNode& TransformNodeFor(
-    LayoutObject& object) {
-  return object.FirstFragment().LocalBorderBoxProperties().Transform();
+static const PropertyTreeState PropertyTreeStateFor(LayoutObject& object) {
+  return object.FirstFragment().LocalBorderBoxProperties();
 }
 
 static void RegionToTracedValue(const Region& region,
@@ -150,34 +149,54 @@
   if (source.IsSVG())
     return;
 
-  const auto& local_xform = TransformNodeFor(painting_layer.GetLayoutObject());
-  const auto& root_xform = TransformNodeFor(*source.View());
+  const auto local_state =
+      PropertyTreeStateFor(painting_layer.GetLayoutObject());
+  const auto root_state = PropertyTreeStateFor(*source.View());
 
-  GeometryMapper::SourceToDestinationRect(local_xform, root_xform, old_rect);
-  GeometryMapper::SourceToDestinationRect(local_xform, root_xform, new_rect);
+  FloatClipRect clip_rect =
+      GeometryMapper::LocalToAncestorClipRect(local_state, root_state);
 
-  if (!old_rect.Intersects(viewport) && !new_rect.Intersects(viewport))
+  // If the clip region is empty, then the resulting layout shift isn't visible
+  // in the viewport so ignore it.
+  if (!clip_rect.IsInfinite() && clip_rect.Rect().IsEmpty())
     return;
 
+  GeometryMapper::SourceToDestinationRect(local_state.Transform(),
+                                          root_state.Transform(), old_rect);
+  GeometryMapper::SourceToDestinationRect(local_state.Transform(),
+                                          root_state.Transform(), new_rect);
+
+  FloatRect clipped_old_rect(old_rect), clipped_new_rect(new_rect);
+  if (!clip_rect.IsInfinite()) {
+    clipped_old_rect.Intersect(clip_rect.Rect());
+    clipped_new_rect.Intersect(clip_rect.Rect());
+  }
+
+  IntRect visible_old_rect = RoundedIntRect(clipped_old_rect);
+  visible_old_rect.Intersect(viewport);
+  IntRect visible_new_rect = RoundedIntRect(clipped_new_rect);
+  visible_new_rect.Intersect(viewport);
+
+  if (visible_old_rect.IsEmpty() && visible_new_rect.IsEmpty())
+    return;
+
+  // Compute move distance based on unclipped rects, to accurately determine how
+  // much the element moved.
+  float move_distance = GetMoveDistance(old_rect, new_rect, source);
+  frame_max_distance_ = std::max(frame_max_distance_, move_distance);
+
 #if DCHECK_IS_ON()
   LocalFrame& frame = frame_view_->GetFrame();
   if (ShouldLog(frame)) {
     DVLOG(2) << "in " << (frame.IsMainFrame() ? "" : "subframe ")
              << frame.GetDocument()->Url().GetString() << ", "
              << source.DebugName() << " moved from " << old_rect.ToString()
-             << " to " << new_rect.ToString();
+             << " to " << new_rect.ToString() << " (visible from "
+             << visible_old_rect.ToString() << " to "
+             << visible_new_rect.ToString() << ")";
   }
 #endif
 
-  frame_max_distance_ = std::max(frame_max_distance_,
-                                 GetMoveDistance(old_rect, new_rect, source));
-
-  IntRect visible_old_rect = RoundedIntRect(old_rect);
-  visible_old_rect.Intersect(viewport);
-
-  IntRect visible_new_rect = RoundedIntRect(new_rect);
-  visible_new_rect.Intersect(viewport);
-
   visible_old_rect.Scale(scale);
   visible_new_rect.Scale(scale);
 
diff --git a/third_party/blink/renderer/core/layout/jank_tracker_test.cc b/third_party/blink/renderer/core/layout/jank_tracker_test.cc
index ed95372..78959ec 100644
--- a/third_party/blink/renderer/core/layout/jank_tracker_test.cc
+++ b/third_party/blink/renderer/core/layout/jank_tracker_test.cc
@@ -276,6 +276,96 @@
   EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score());
 }
 
+TEST_F(JankTrackerTest, FullyClippedVisualRect) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      body { margin: 0; }
+      #clip { width: 0px; height: 600px; overflow: hidden; }
+      #j { position: relative; width: 300px; height: 200px; }
+    </style>
+    <div id='clip'><div id='j'></div></div>
+  )HTML");
+
+  GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
+                                                  AtomicString("top: 200px"));
+  UpdateAllLifecyclePhases();
+  EXPECT_FLOAT_EQ(0.0, GetJankTracker().Score());
+}
+
+TEST_F(JankTrackerTest, PartiallyClippedVisualRect) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      body { margin: 0; }
+      #clip { width: 150px; height: 600px; overflow: hidden; }
+      #j { position: relative; width: 300px; height: 200px; }
+    </style>
+    <div id='clip'><div id='j'></div></div>
+  )HTML");
+
+  GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
+                                                  AtomicString("top: 200px"));
+  UpdateAllLifecyclePhases();
+  // (clipped width 150px) * (height 200 + movement 200) / (800 * 600 viewport)
+  EXPECT_FLOAT_EQ(0.125, GetJankTracker().Score());
+}
+
+TEST_F(JankTrackerTest, MultiClipVisualRect) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      body { margin: 0; }
+      #outer { width: 200px; height: 600px; overflow: hidden; }
+      #inner { width: 300px; height: 150px; overflow: hidden; }
+      #j { position: relative; width: 300px; height: 600px; }
+    </style>
+    <div id='outer'><div id='inner'><div id='j'></div></div></div>
+  )HTML");
+
+  GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
+                                                  AtomicString("top: -200px"));
+  UpdateAllLifecyclePhases();
+  // Note that, while the element moves up 200px, its visibility is
+  // clipped at 0px,150px height, so the additional 200px of vertical
+  // move distance is not included in the score.
+  // (clip width 200) * (clip height 150) / (800 * 600 viewport)
+  EXPECT_FLOAT_EQ(0.0625, GetJankTracker().Score());
+  EXPECT_FLOAT_EQ(200.0, GetJankTracker().OverallMaxDistance());
+}
+
+TEST_F(JankTrackerTest, ShiftOutsideViewport) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      body { margin: 0; }
+      #j { position: relative; width: 600px; height: 200px; top: 600px; }
+    </style>
+    <div id='j'></div>
+  )HTML");
+
+  GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
+                                                  AtomicString("top: 800px"));
+  UpdateAllLifecyclePhases();
+  // Since the element moves entirely outside of the viewport, it shouldn't
+  // generate a score.
+  EXPECT_FLOAT_EQ(0.0, GetJankTracker().Score());
+}
+
+TEST_F(JankTrackerTest, ShiftInToViewport) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      body { margin: 0; }
+      #j { position: relative; width: 600px; height: 200px; top: 600px; }
+    </style>
+    <div id='j'></div>
+  )HTML");
+
+  GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
+                                                  AtomicString("top: 400px"));
+  UpdateAllLifecyclePhases();
+  // The element moves from outside the viewport to within the viewport, which
+  // should generate jank.
+  // (width 600) * (height 0 + move 200) / (800 * 600 viewport)
+  EXPECT_FLOAT_EQ(0.25, GetJankTracker().Score());
+}
+
 class JankTrackerSimTest : public SimTest {};
 
 TEST_F(JankTrackerSimTest, SubframeWeighting) {
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index dabbe34..dac82df 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -343,7 +343,8 @@
 }
 
 bool ImageLoader::ShouldUpdateOnInsertedInto(
-    ContainerNode& insertion_point) const {
+    ContainerNode& insertion_point,
+    network::mojom::ReferrerPolicy referrer_policy) const {
   // If we're being inserted into a disconnected tree, we don't need to update.
   if (!insertion_point.isConnected())
     return false;
@@ -355,10 +356,15 @@
   if (element_->GetDocument().ValidBaseElementURL() != last_base_element_url_)
     return true;
 
-  // Finally, try to update if we're idle (that is, we have neither the image
-  // contents nor any activity). This could be an indication that we skipped a
-  // previous load when inserted into an inactive document.
-  return !image_content_ && !HasPendingActivity();
+  // If we already have image content, then we don't need an update.
+  if (image_content_)
+    return false;
+
+  // Finally, try to update if we're idle. This could be an indication that we
+  // skipped a previous load when inserted into an inactive document. Note that
+  // if we're not idle, we should also update our referrer policy if it has
+  // changed.
+  return !HasPendingActivity() || referrer_policy != last_referrer_policy_;
 }
 
 void ImageLoader::ClearImage() {
@@ -562,17 +568,16 @@
         const LazyLoadImageEligibility lazy_load_image_eligibility =
             DetermineLazyLoadImageEligibility(*frame, *html_image,
                                               params.Url());
-        const auto lazy_load_image_enabled_state =
-            frame->GetLazyLoadImageEnabledState();
+        const auto lazy_load_image_setting = frame->GetLazyLoadImageSetting();
 
         if ((lazy_load_image_eligibility ==
                  LazyLoadImageEligibility::kEnabledExplicit &&
-             lazy_load_image_enabled_state !=
-                 LocalFrame::LazyLoadImageEnabledState::kDisabled) ||
+             lazy_load_image_setting !=
+                 LocalFrame::LazyLoadImageSetting::kDisabled) ||
             (lazy_load_image_eligibility ==
                  LazyLoadImageEligibility::kEnabledAutomatic &&
-             lazy_load_image_enabled_state ==
-                 LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic)) {
+             lazy_load_image_setting ==
+                 LocalFrame::LazyLoadImageSetting::kEnabledAutomatic)) {
           if (IsDimensionAbsoluteLarge(*html_image)) {
             params.SetLazyImageDeferred();
           } else {
@@ -659,6 +664,7 @@
   suppress_error_events_ = (update_behavior == kUpdateSizeChanged);
   last_base_element_url_ =
       element_->GetDocument().ValidBaseElementURL().GetString();
+  last_referrer_policy_ = referrer_policy;
 
   if (update_behavior == kUpdateIgnorePreviousError)
     ClearFailedLoadURL();
diff --git a/third_party/blink/renderer/core/loader/image_loader.h b/third_party/blink/renderer/core/loader/image_loader.h
index 26093ff..19231a8 100644
--- a/third_party/blink/renderer/core/loader/image_loader.h
+++ b/third_party/blink/renderer/core/loader/image_loader.h
@@ -90,7 +90,10 @@
 
   // Returns true if this loader should be updated via UpdateFromElement() when
   // being inserted into a new parent; returns false otherwise.
-  bool ShouldUpdateOnInsertedInto(ContainerNode& insertion_point) const;
+  bool ShouldUpdateOnInsertedInto(
+      ContainerNode& insertion_point,
+      network::mojom::ReferrerPolicy referrer_policy =
+          network::mojom::ReferrerPolicy::kDefault) const;
 
   // Cancels pending load events, and doesn't dispatch new ones.
   // Note: ClearImage/SetImage.*() are not a simple setter.
@@ -208,6 +211,8 @@
   Member<ImageResource> image_resource_for_image_document_;
 
   String last_base_element_url_;
+  network::mojom::ReferrerPolicy last_referrer_policy_ =
+      network::mojom::ReferrerPolicy::kDefault;
   AtomicString failed_load_url_;
   base::WeakPtr<Task> pending_task_;  // owned by Microtask
   std::unique_ptr<IncrementLoadEventDelayCount>
diff --git a/third_party/blink/renderer/core/loader/image_loader_test.cc b/third_party/blink/renderer/core/loader/image_loader_test.cc
new file mode 100644
index 0000000..95d02c94
--- /dev/null
+++ b/third_party/blink/renderer/core/loader/image_loader_test.cc
@@ -0,0 +1,52 @@
+// Copyright 2019 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 "third_party/blink/renderer/core/loader/image_loader.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/html/html_image_loader.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+
+namespace blink {
+
+class ImageLoaderTest : public RenderingTest {};
+
+TEST_F(ImageLoaderTest, ReferrerPolicyChangeCausesUpdateOnInsert) {
+  SetHtmlInnerHTML(R"HTML(
+    <img id="test" src="test.png">
+  )HTML");
+
+  auto* element = GetDocument().getElementById("test");
+  ASSERT_TRUE(element);
+
+  auto* loader = MakeGarbageCollected<HTMLImageLoader>(element);
+  ASSERT_TRUE(loader);
+
+  // We should already be collected, so UpdateFromElement() would cause some
+  // pending activity.
+  loader->UpdateFromElement();
+  ASSERT_TRUE(loader->HasPendingActivity());
+
+  // We don't need an update, since we're already loading an image.
+  EXPECT_FALSE(loader->ShouldUpdateOnInsertedInto(*element));
+
+  // However, if the referrer policy changes, then we should need an update.
+  EXPECT_TRUE(loader->ShouldUpdateOnInsertedInto(
+      *element, network::mojom::ReferrerPolicy::kNever));
+
+  // Changing referrer policy.
+  loader->UpdateFromElement(ImageLoader::kUpdateNormal,
+                            network::mojom::ReferrerPolicy::kNever);
+
+  // Now, we don't need an update with the latest referrer policy.
+  EXPECT_FALSE(loader->ShouldUpdateOnInsertedInto(
+      *element, network::mojom::ReferrerPolicy::kNever));
+
+  // But we do want an update if the referrer policy changes back to what it was
+  // before.
+  EXPECT_TRUE(loader->ShouldUpdateOnInsertedInto(
+      *element, network::mojom::ReferrerPolicy::kDefault));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index ae57e30e..29d5d8f 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1642,11 +1642,15 @@
 void PaintLayer::UpdateScrollableArea() {
   if (RequiresScrollableArea() && !scrollable_area_) {
     scrollable_area_ = PaintLayerScrollableArea::Create(*this);
-    Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
+    if (Compositor()) {
+      Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
+    }
   } else if (!RequiresScrollableArea() && scrollable_area_) {
     scrollable_area_->Dispose();
     scrollable_area_.Clear();
-    Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
+    if (Compositor()) {
+      Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
+    }
   }
 }
 
@@ -2820,8 +2824,11 @@
       // work with CompositeAfterPaint. Some transform tree changes may still
       // produce incorrect behavior from JankTracker (see discussion on review
       // thread of http://crrev.com/c/1636403).
-      Compositor()->ForceRecomputeVisualRectsIncludingNonCompositingDescendants(
-          layout_object_);
+      if (Compositor()) {
+        Compositor()
+            ->ForceRecomputeVisualRectsIncludingNonCompositingDescendants(
+                layout_object_);
+      }
     }
   } else {
     // We need to make sure our decendants get a geometry update. In principle,
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test.cc b/third_party/blink/renderer/core/workers/worker_thread_test.cc
index adb1f60..26897d1 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread_test.cc
@@ -23,6 +23,22 @@
 #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
+// TODO(crbug.com/960985): Fix memory leaks in tests and re-enable on LSAN.
+#ifdef LEAK_SANITIZER
+#define MAYBE_TerminateFrozenScript DISABLED_TerminateFrozenScript
+#define MAYBE_NestedPauseFreeze DISABLED_NestedPauseFreeze
+#define MAYBE_TerminateWhileWorkerPausedByDebugger \
+  DISABLED_TerminateWhileWorkerPausedByDebugger
+#define MAYBE_NestedPauseFreezeNoInterrupts \
+  DISABLED_NestedPauseFreezeNoInterrupts
+#else
+#define MAYBE_TerminateFrozenScript TerminateFrozenScript
+#define MAYBE_NestedPauseFreeze NestedPauseFreeze
+#define MAYBE_TerminateWhileWorkerPausedByDebugger \
+  TerminateWhileWorkerPausedByDebugger
+#define MAYBE_NestedPauseFreezeNoInterrupts NestedPauseFreezeNoInterrupts
+#endif
+
 using testing::_;
 using testing::AtMost;
 
@@ -514,7 +530,7 @@
 }
 
 // Tests terminating a worker when debugger is paused.
-TEST_F(WorkerThreadTest, TerminateWhileWorkerPausedByDebugger) {
+TEST_F(WorkerThreadTest, MAYBE_TerminateWhileWorkerPausedByDebugger) {
   constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10);
   SetForcibleTerminationDelay(kDelay);
 
@@ -535,7 +551,7 @@
   EXPECT_EQ(ExitCode::kAsyncForciblyTerminated, GetExitCode());
 }
 
-TEST_F(WorkerThreadTest, TerminateFrozenScript) {
+TEST_F(WorkerThreadTest, MAYBE_TerminateFrozenScript) {
   constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10);
   SetForcibleTerminationDelay(kDelay);
 
@@ -563,7 +579,7 @@
   EXPECT_EQ(ExitCode::kAsyncForciblyTerminated, GetExitCode());
 }
 
-TEST_F(WorkerThreadTest, NestedPauseFreeze) {
+TEST_F(WorkerThreadTest, MAYBE_NestedPauseFreeze) {
   constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10);
   SetForcibleTerminationDelay(kDelay);
 
@@ -602,7 +618,7 @@
   EXPECT_EQ(ExitCode::kAsyncForciblyTerminated, GetExitCode());
 }
 
-TEST_F(WorkerThreadTest, NestedPauseFreezeNoInterrupts) {
+TEST_F(WorkerThreadTest, MAYBE_NestedPauseFreezeNoInterrupts) {
   constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10);
   SetForcibleTerminationDelay(kDelay);
 
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js
index 584e5791..5443b9c 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js
@@ -539,7 +539,7 @@
         valueStyle = 'null';
         break;
       case 'array':
-        value = (value || ls`(internal array)`) + '[]';
+        value = value ? `${value}[]` : ls`(internal array)[]`;
         break;
     }
     return this._createObjectCellWithValue(valueStyle, value);
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
index b46a803..9b5bb21d 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
@@ -159,9 +159,6 @@
   <message name="IDS_DEVTOOLS_54afafa921428eed23978f633af4ce99" desc="">
     Allocation sampling
   </message>
-  <message name="IDS_DEVTOOLS_57b1250c3cbec60c67810ac83afb2c64" desc="">
-    (internal array)
-  </message>
   <message name="IDS_DEVTOOLS_5dd1d9f5baa173ae9267b3631da17a83" desc="">
     Self size
   </message>
@@ -261,6 +258,9 @@
   <message name="IDS_DEVTOOLS_9206de0db462c24e8a56433e2f7b7c75" desc="">
     Heap Snapshot
   </message>
+  <message name="IDS_DEVTOOLS_9274c1c81e169e786d1dc76b464b9089" desc="">
+    (internal array)[]
+  </message>
   <message name="IDS_DEVTOOLS_959f661cf87130eea5c8e5671c013741" desc="">
     Objects allocated before <ph name="LIST_I__TITLE">$1s</ph>
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js b/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
index 6369715..6c91449 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
@@ -1376,9 +1376,10 @@
   }
 
   _updateTooltip() {
-    this.tooltip = Common.UIString('Version') + ': ' + this._database.version;
     if (Object.keys(this._idbObjectStoreTreeElements).length === 0)
-      this.tooltip += ls` (empty)`;
+      this.tooltip = ls`Version: ${this._database.version} (empty)`;
+    else
+      this.tooltip = ls`Version: ${this._database.version}`;
   }
 
   /**
@@ -1510,7 +1511,7 @@
 
   _updateTooltip() {
     const keyPathString = this._objectStore.keyPathString;
-    let tooltipString = keyPathString !== null ? (Common.UIString('Key path: ') + keyPathString) : '';
+    let tooltipString = keyPathString !== null ? ls`Key path: ${keyPathString}` : '';
     if (this._objectStore.autoIncrement)
       tooltipString += '\n' + Common.UIString('autoIncrement');
     this.tooltip = tooltipString;
@@ -1603,7 +1604,7 @@
   _updateTooltip() {
     const tooltipLines = [];
     const keyPathString = this._index.keyPathString;
-    tooltipLines.push(Common.UIString('Key path: ') + keyPathString);
+    tooltipLines.push(ls`Key path: ${keyPathString}`);
     if (this._index.unique)
       tooltipLines.push(Common.UIString('unique'));
     if (this._index.multiEntry)
@@ -1687,7 +1688,7 @@
     super(storagePanel, cookieDomain ? cookieDomain : Common.UIString('Local Files'), false);
     this._target = frame.resourceTreeModel().target();
     this._cookieDomain = cookieDomain;
-    this.tooltip = ls`cookies used by frames from ` + cookieDomain;
+    this.tooltip = ls`cookies used by frames from ${cookieDomain}`;
     const icon = UI.Icon.create('mediumicon-cookie', 'resource-tree-item');
     this.setLeadingIcons([icon]);
   }
diff --git a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
index 48d0c4a..152ba10 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
@@ -27,15 +27,15 @@
   <message name="IDS_DEVTOOLS_15fa5e18dc110ac96ef35fd19abc78b3" desc="">
     Time Cached
   </message>
-  <message name="IDS_DEVTOOLS_1722d5b7f86af20cbd8d1981f23a15a3" desc="">
-    cookies used by frames from 
-  </message>
   <message name="IDS_DEVTOOLS_1818d506396d77b3d035f719885c4cd1" desc="">
     focus
   </message>
   <message name="IDS_DEVTOOLS_1bea0f12b50db07ee7f2265b790417ff" desc="">
     Installability
   </message>
+  <message name="IDS_DEVTOOLS_1e9cf6d18aa0124e44d013131c79b0c1" desc="">
+    Version: <ph name="THIS__DATABASE_VERSION">$1s</ph> (empty)
+  </message>
   <message name="IDS_DEVTOOLS_1fece652e0dde2e00d1a7662f081dd71" desc="">
     The &quot;<ph name="THIS_TABLENAME">$1s</ph>&quot;
 table is empty.
@@ -73,6 +73,9 @@
   <message name="IDS_DEVTOOLS_3a771376134eb624f3e1fdd3d92d9f4c" desc="">
     Select a cache entry above to preview
   </message>
+  <message name="IDS_DEVTOOLS_3af55819bc887213086b7ba5c5e25042" desc="">
+    Version: <ph name="THIS__DATABASE_VERSION">$1s</ph>
+  </message>
   <message name="IDS_DEVTOOLS_3afd748bcc6315d69cff002ec6c377ed" desc="">
     <ph name="THIS__REGISTRATION_ERRORS_LENGTH">$1s</ph> registration errors
   </message>
@@ -184,6 +187,9 @@
   <message name="IDS_DEVTOOLS_8f67973007158337346584551b093be8" desc="">
     Icons
   </message>
+  <message name="IDS_DEVTOOLS_9093b5a47841e196aeee81e9441eb23b" desc="">
+    cookies used by frames from <ph name="COOKIEDOMAIN">$1s</ph>
+  </message>
   <message name="IDS_DEVTOOLS_90e4c7584668933aebe8cf1cbccfe82d" desc="">
     Please confirm delete of &quot;<ph name="THIS__DATABASE_DATABASEID_NAME">$1s</ph>&quot; database.
   </message>
@@ -206,9 +212,6 @@
   <message name="IDS_DEVTOOLS_9b2a7456cec10d8b5ab8ce656598320b" desc="">
     Key path: 
   </message>
-  <message name="IDS_DEVTOOLS_9b790a1c94937c437f9801d3c970efa7" desc="">
-     (empty)
-  </message>
   <message name="IDS_DEVTOOLS_9c6a9d9f033001a9b3104984d319563b" desc="">
     Push
   </message>
@@ -344,6 +347,9 @@
   <message name="IDS_DEVTOOLS_da131b6bd53f501c1323af4b198740d2" desc="">
     Push Messaging
   </message>
+  <message name="IDS_DEVTOOLS_db01fae7397127fd9712b2efe6f8346f" desc="">
+    Key path: <ph name="KEYPATHSTRING">$1s</ph>
+  </message>
   <message name="IDS_DEVTOOLS_dc105ac52c237f9f09c74b0767f64f74" desc="">
     Content-Type
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/settings/FrameworkBlackboxSettingsTab.js b/third_party/blink/renderer/devtools/front_end/settings/FrameworkBlackboxSettingsTab.js
index eee7f01..c6571053 100644
--- a/third_party/blink/renderer/devtools/front_end/settings/FrameworkBlackboxSettingsTab.js
+++ b/third_party/blink/renderer/devtools/front_end/settings/FrameworkBlackboxSettingsTab.js
@@ -73,8 +73,7 @@
     const element = createElementWithClass('div', 'blackbox-list-item');
     const pattern = element.createChild('div', 'blackbox-pattern');
     pattern.textContent = item.pattern;
-    pattern.title = ls`Blackbox scripts whose names match` +
-        ' \'' + item.pattern + '\'';
+    pattern.title = ls`Blackbox scripts whose names match '${item.pattern}'`;
     element.createChild('div', 'blackbox-separator');
     element.createChild('div', 'blackbox-behavior').textContent =
         item.disabled ? this._disabledLabel : this._blackboxLabel;
diff --git a/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp b/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp
index 594f0c9..bfaeef6 100644
--- a/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp
@@ -15,6 +15,9 @@
   <message name="IDS_DEVTOOLS_4829262cecb9828817b33e0f9c907f91" desc="">
     Experiments
   </message>
+  <message name="IDS_DEVTOOLS_4a3aaf1edf15288e9ff3994088e2277a" desc="">
+    Blackbox scripts whose names match &apos;<ph name="ITEM_PATTERN">$1s</ph>&apos;
+  </message>
   <message name="IDS_DEVTOOLS_57391192dfa1f247ad015a0fe2eca48e" desc="">
     Pattern
   </message>
@@ -36,9 +39,6 @@
   <message name="IDS_DEVTOOLS_c9deece3e6de26d07ef6409b96f21fd0" desc="">
     Blackboxing
   </message>
-  <message name="IDS_DEVTOOLS_ce85958ff51e825dcf797d0451b96b71" desc="">
-    Blackbox scripts whose names match
-  </message>
   <message name="IDS_DEVTOOLS_d0834fcec6337785ee749c8f5464f6f6" desc="">
     Preferences
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
index 2088c4f6..d2aa44d 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
@@ -662,10 +662,10 @@
 UI.asyncStackTraceLabel = function(description) {
   if (description) {
     if (description === 'Promise.resolve')
-      description = Common.UIString('Promise resolved');
+      return ls`Promise resolved (async)`;
     else if (description === 'Promise.reject')
-      description = Common.UIString('Promise rejected');
-    return description + ' ' + Common.UIString('(async)');
+      return ls`Promise rejected (async)`;
+    return ls`${description} (async)`;
   }
   return Common.UIString('Async Call');
 };
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp b/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
index 263028b..0088e73 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
@@ -24,8 +24,8 @@
   <message name="IDS_DEVTOOLS_0f8854fb5bc175b0cd33a1e8518b366f" desc="">
     Switch to pan mode
   </message>
-  <message name="IDS_DEVTOOLS_10f0bad7213cafe18fb1cf55180a11da" desc="">
-    (async)
+  <message name="IDS_DEVTOOLS_103e6e056329361a5d169154f52771d0" desc="">
+    <ph name="DESCRIPTION">$1s</ph> (async)
   </message>
   <message name="IDS_DEVTOOLS_1299e7f10b9d0110e7fa5368d1307500" desc="">
     long text was truncated (<ph name="TOTALBYTES">$1s</ph>)
@@ -123,9 +123,6 @@
   <message name="IDS_DEVTOOLS_678b7bbae3b9b67cee8bce1fac3067d7" desc="">
     Full list of DevTools keyboard shortcuts and gestures
   </message>
-  <message name="IDS_DEVTOOLS_6a5fa3625d6317cd74f686720d9c411e" desc="">
-    Promise rejected
-  </message>
   <message name="IDS_DEVTOOLS_6e0e121820384f135321cb766273f4da" desc="">
     Soft undo
   </message>
@@ -228,6 +225,9 @@
   <message name="IDS_DEVTOOLS_b508f4cb8252d1d90689d1a61344c8fc" desc="">
     <ph name="ITEM_LABEL">$1s</ph>, checked, <ph name="ITEM_SHORTCUT">$2s</ph>
   </message>
+  <message name="IDS_DEVTOOLS_b6da8d6392710f305d7176a134971514" desc="">
+    Promise rejected (async)
+  </message>
   <message name="IDS_DEVTOOLS_b79165bf6163186253ed04568e1aee11" desc="">
     Pause/ Continue
   </message>
@@ -237,6 +237,9 @@
   <message name="IDS_DEVTOOLS_b7ad6a024765bbfbff22dedf9afe0d17" desc="">
     Jump to previous editing location
   </message>
+  <message name="IDS_DEVTOOLS_b94d1b250ba62f06dadf0edc4a947f5c" desc="">
+    Promise resolved (async)
+  </message>
   <message name="IDS_DEVTOOLS_bc39d641a1e16fb73f2ce5520d250ed0" desc="">
     Go to member
   </message>
@@ -249,9 +252,6 @@
   <message name="IDS_DEVTOOLS_d06337cfbcc777e0a7caa50ef1a9986a" desc="">
     More tabs
   </message>
-  <message name="IDS_DEVTOOLS_d2923d7d060c61c17d06c22166b2be41" desc="">
-    Promise resolved
-  </message>
   <message name="IDS_DEVTOOLS_d9f57fb1f634a4e047fd69b574434c7f" desc="">
     Evaluate selection in console
   </message>
diff --git a/third_party/blink/renderer/devtools/scripts/check_localizability.js b/third_party/blink/renderer/devtools/scripts/check_localizability.js
index 30102a70..e55d741 100644
--- a/third_party/blink/renderer/devtools/scripts/check_localizability.js
+++ b/third_party/blink/renderer/devtools/scripts/check_localizability.js
@@ -88,15 +88,30 @@
 
 /**
  * Recursively check if there is concatenation to localization call.
- * Concatenation is allowed between localized strings and non-alphabetic strings.
- * It is not allowed between a localized string and a word.
- * Example (allowed): ls`Status Code` + ": "
- * Example (disallowed): ls`Status` + " Code" + ": "
+ * Concatenation is allowed between localized strings and strings that
+ * don't contain letters.
+ * Example (allowed): ls`Status code: ${statusCode}`
+ * Example (allowed): ls`Status code` + ': '
+ * Example (disallowed): ls`Status code: ` + statusCode
+ * Example (disallowed): ls`Status ` + 'code'
  */
 function checkConcatenation(parentNode, node, filePath, errors) {
-  function isWord(node) {
-    return (node.type === esprimaTypes.LITERAL && !!node.value.match(/[a-z]/i));
+  function isConcatenationDisallowed(node) {
+    if (node.type !== esprimaTypes.LITERAL && node.type !== esprimaTypes.TEMP_LITERAL)
+      return true;
+
+    let value;
+    if (node.type === esprimaTypes.LITERAL)
+      value = node.value;
+    else if (node.type === esprimaTypes.TEMP_LITERAL && node.expressions.length === 0)
+      value = node.quasis[0].value.cooked;
+
+    if (!value || typeof value !== 'string')
+      return true;
+
+    return value.match(/[a-z]/i) !== null;
   }
+
   function isConcatenation(node) {
     return (node !== undefined && node.type === esprimaTypes.BI_EXPR && node.operator === '+');
   }
@@ -107,11 +122,12 @@
   if (isConcatenation(node)) {
     const concatenatedNodes = [];
     buildConcatenatedNodesList(node, concatenatedNodes);
-    const hasLocalizationCall =
-        !!concatenatedNodes.find(currentNode => localizationUtils.isLocalizationCall(currentNode));
+    const nonLocalizationCalls = concatenatedNodes.filter(node => !localizationUtils.isLocalizationCall(node));
+    const hasLocalizationCall = nonLocalizationCalls.length !== concatenatedNodes.length;
     if (hasLocalizationCall) {
-      const hasAlphabeticLiteral = !!concatenatedNodes.find(currentNode => isWord(currentNode));
-      if (hasAlphabeticLiteral) {
+      // concatenation with localization call
+      const hasConcatenationViolation = nonLocalizationCalls.some(isConcatenationDisallowed);
+      if (hasConcatenationViolation) {
         const code = escodegen.generate(node);
         addError(
             `${filePath}${
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 1b93b8db..31460709 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1399,12 +1399,12 @@
 }
 
 AXObject* AXNodeObject::InPageLinkTarget() const {
-  if (!node_ || !IsHTMLAnchorElement(node_) || !GetDocument())
+  if (!IsAnchor() || !GetDocument())
     return AXObject::InPageLinkTarget();
 
-  HTMLAnchorElement* anchor = ToHTMLAnchorElement(node_.Get());
+  Element* anchor = AnchorElement();
   DCHECK(anchor);
-  KURL link_url = anchor->Href();
+  KURL link_url = anchor->HrefURL();
   if (!link_url.IsValid())
     return AXObject::InPageLinkTarget();
   String fragment = link_url.FragmentIdentifier();
@@ -1777,7 +1777,12 @@
 }
 
 KURL AXNodeObject::Url() const {
-  if (IsAnchor() && IsHTMLAnchorElement(GetNode())) {
+  if (IsAnchor()) {
+    // Some non-HTML elements, most notably SVG <a> elements, can act as
+    // links/anchors.
+    if (!IsHTMLAnchorElement(GetNode()))
+      return AnchorElement()->HrefURL();
+
     if (HTMLAnchorElement* anchor = ToHTMLAnchorElementOrNull(AnchorElement()))
       return anchor->Href();
   }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
index 9f433bca..dd2de0bc 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
@@ -7,6 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
 
 namespace blink {
 namespace test {
@@ -165,5 +166,60 @@
   EXPECT_EQ(GetAXObjectCache().InOrderTraversalBegin(), iter);
 }
 
+TEST_F(AccessibilityTest, AxNodeObjectContainsHtmlAnchorElementUrl) {
+  SetBodyInnerHTML(R"HTML(<a id="anchor" href="http://test.com">link</a>)HTML");
+
+  const AXObject* root = GetAXRootObject();
+  ASSERT_NE(nullptr, root);
+  const AXObject* anchor = GetAXObjectByElementId("anchor");
+  ASSERT_NE(nullptr, anchor);
+
+  // Passing a malformed string to KURL returns an empty URL, so verify the
+  // AXObject's URL is non-empty first to catch errors in the test itself.
+  EXPECT_FALSE(anchor->Url().IsEmpty());
+  EXPECT_EQ(anchor->Url(), KURL("http://test.com"));
+}
+
+TEST_F(AccessibilityTest, AxNodeObjectContainsSvgAnchorElementUrl) {
+  SetBodyInnerHTML(R"HTML(
+    <svg>
+      <a id="anchor" xlink:href="http://test.com"></a>
+    </svg>
+  )HTML");
+
+  const AXObject* root = GetAXRootObject();
+  ASSERT_NE(nullptr, root);
+  const AXObject* anchor = GetAXObjectByElementId("anchor");
+  ASSERT_NE(nullptr, anchor);
+
+  EXPECT_FALSE(anchor->Url().IsEmpty());
+  EXPECT_EQ(anchor->Url(), KURL("http://test.com"));
+}
+
+TEST_F(AccessibilityTest, AxNodeObjectContainsImageUrl) {
+  SetBodyInnerHTML(R"HTML(<img id="anchor" src="http://test.png" />)HTML");
+
+  const AXObject* root = GetAXRootObject();
+  ASSERT_NE(nullptr, root);
+  const AXObject* anchor = GetAXObjectByElementId("anchor");
+  ASSERT_NE(nullptr, anchor);
+
+  EXPECT_FALSE(anchor->Url().IsEmpty());
+  EXPECT_EQ(anchor->Url(), KURL("http://test.png"));
+}
+
+TEST_F(AccessibilityTest, AxNodeObjectContainsInPageLinkTarget) {
+  GetDocument().SetBaseURLOverride(KURL("http://test.com"));
+  SetBodyInnerHTML(R"HTML(<a id="anchor" href="#target">link</a>)HTML");
+
+  const AXObject* root = GetAXRootObject();
+  ASSERT_NE(nullptr, root);
+  const AXObject* anchor = GetAXObjectByElementId("anchor");
+  ASSERT_NE(nullptr, anchor);
+
+  EXPECT_FALSE(anchor->Url().IsEmpty());
+  EXPECT_EQ(anchor->Url(), KURL("http://test.com/#target"));
+}
+
 }  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc
index dac0583..96fa3ae 100644
--- a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc
+++ b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc
@@ -81,6 +81,7 @@
 }
 
 ScriptPromise ContactsManager::select(ScriptState* script_state,
+                                      const Vector<String>& properties,
                                       ContactsSelectOptions* options) {
   Document* document = To<Document>(ExecutionContext::From(script_state));
   if (!LocalFrame::HasTransientUserActivation(document ? document->GetFrame()
@@ -91,7 +92,7 @@
                           "A user gesture is required to call this method"));
   }
 
-  if (!options->hasProperties() || !options->properties().size()) {
+  if (properties.IsEmpty()) {
     return ScriptPromise::Reject(script_state,
                                  V8ThrowException::CreateTypeError(
                                      script_state->GetIsolate(),
@@ -105,7 +106,7 @@
   bool include_emails = false;
   bool include_tel = false;
 
-  for (const String& property : options->properties()) {
+  for (const String& property : properties) {
     if (property == "name")
       include_names = true;
     else if (property == "email")
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.h b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.h
index 9bc77135..9d016f1 100644
--- a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.h
+++ b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/modules/contacts_picker/contacts_select_options.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -26,6 +27,7 @@
 
   // Web-exposed function defined in the IDL file.
   ScriptPromise select(ScriptState* script_state,
+                       const Vector<String>& properties,
                        ContactsSelectOptions* options);
 
  private:
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.idl b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.idl
index 73ac590..a208a78 100644
--- a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.idl
+++ b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.idl
@@ -9,5 +9,5 @@
     SecureContext,
     RuntimeEnabled=ContactsManager
 ] interface ContactsManager {
-    [CallWith=ScriptState] Promise<sequence<ContactInfo>> select(ContactsSelectOptions options);
+    [CallWith=ScriptState] Promise<sequence<ContactInfo>> select(sequence<ContactProperty> properties, optional ContactsSelectOptions options);
 };
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_select_options.idl b/third_party/blink/renderer/modules/contacts_picker/contacts_select_options.idl
index 9acfe325..4cab707d 100644
--- a/third_party/blink/renderer/modules/contacts_picker/contacts_select_options.idl
+++ b/third_party/blink/renderer/modules/contacts_picker/contacts_select_options.idl
@@ -7,6 +7,5 @@
 enum ContactProperty { "email", "name", "tel" };
 
 dictionary ContactsSelectOptions {
-    sequence<ContactProperty> properties;
     boolean multiple = false;
 };
diff --git a/third_party/blink/renderer/modules/contacts_picker/navigator_contacts.idl b/third_party/blink/renderer/modules/contacts_picker/navigator_contacts.idl
index bb47e9c..65bed25 100644
--- a/third_party/blink/renderer/modules/contacts_picker/navigator_contacts.idl
+++ b/third_party/blink/renderer/modules/contacts_picker/navigator_contacts.idl
@@ -8,5 +8,5 @@
     Exposed=Window,
     ImplementedAs=NavigatorContacts
 ] partial interface Navigator {
-    [SecureContext, RuntimeEnabled=ContactsManager] readonly attribute ContactsManager contacts;
+    [SecureContext, SameObject, RuntimeEnabled=ContactsManager] readonly attribute ContactsManager contacts;
 };
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index ece3db0b..560f790 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -292,7 +292,8 @@
           "this device unless the device is secured "
           "with a screen lock.");
     case CredentialManagerError::ABORT:
-      return MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError);
+      return MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError,
+                                                "Request has been aborted.");
     case CredentialManagerError::UNKNOWN:
       return MakeGarbageCollected<DOMException>(
           DOMExceptionCode::kNotReadableError,
@@ -523,8 +524,8 @@
 
     if (options->hasSignal()) {
       if (options->signal()->aborted()) {
-        resolver->Reject(
-            MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
+        resolver->Reject(MakeGarbageCollected<DOMException>(
+            DOMExceptionCode::kAbortError, "Request has been aborted."));
         return promise;
       }
       options->signal()->AddAlgorithm(
@@ -705,8 +706,8 @@
 
     if (options->hasSignal()) {
       if (options->signal()->aborted()) {
-        resolver->Reject(
-            MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
+        resolver->Reject(MakeGarbageCollected<DOMException>(
+            DOMExceptionCode::kAbortError, "Request has been aborted."));
         return promise;
       }
       options->signal()->AddAlgorithm(
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
index 70a1b7a..2550ad91 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
@@ -145,16 +145,46 @@
   Supplement<LocalDOMWindow>::Trace(visitor);
 }
 
+void PaintWorklet::RegisterCSSPaintDefinition(const String& name,
+                                              CSSPaintDefinition* definition,
+                                              ExceptionState& exception_state) {
+  if (document_definition_map_.Contains(name)) {
+    DocumentPaintDefinition* existing_document_definition =
+        document_definition_map_.at(name);
+    if (existing_document_definition == kInvalidDocumentPaintDefinition)
+      return;
+    if (!existing_document_definition->RegisterAdditionalPaintDefinition(
+            *definition)) {
+      document_definition_map_.Set(name, kInvalidDocumentPaintDefinition);
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kNotSupportedError,
+          "A class with name:'" + name +
+              "' was registered with a different definition.");
+      return;
+    }
+    // Notify the generator ready only when register paint is called the
+    // second time with the same |name| (i.e. there is already a document
+    // definition associated with |name|
+    if (existing_document_definition->GetRegisteredDefinitionCount() ==
+        PaintWorklet::kNumGlobalScopes)
+      pending_generator_registry_->NotifyGeneratorReady(name);
+  } else {
+    DocumentPaintDefinition* document_definition =
+        MakeGarbageCollected<DocumentPaintDefinition>(definition);
+    document_definition_map_.Set(name, document_definition);
+  }
+}
+
 void PaintWorklet::RegisterMainThreadDocumentPaintDefinition(
     const String& name,
     Vector<CSSPropertyID> native_properties,
     Vector<String> custom_properties,
-    std::unique_ptr<Vector<CSSSyntaxDescriptor>> input_argument_types,
+    Vector<CSSSyntaxDescriptor> input_argument_types,
     double alpha) {
   DCHECK(!main_thread_document_definition_map_.Contains(name));
   auto definition = std::make_unique<MainThreadDocumentPaintDefinition>(
       std::move(native_properties), std::move(custom_properties),
-      std::move(*input_argument_types), alpha);
+      std::move(input_argument_types), alpha);
   main_thread_document_definition_map_.insert(name, std::move(definition));
   pending_generator_registry_->NotifyGeneratorReady(name);
 }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.h b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
index 3951f907..cbf1084b 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
@@ -60,6 +60,10 @@
     return document_definition_map_;
   }
 
+  void RegisterCSSPaintDefinition(const String& name,
+                                  CSSPaintDefinition*,
+                                  ExceptionState&);
+
   // Used for off-thread CSS Paint. In this mode we are not responsible for
   // tracking whether a definition is valid - this method should only be called
   // once all global scopes have registered the same
@@ -68,7 +72,7 @@
       const String& name,
       Vector<CSSPropertyID> native_properties,
       Vector<String> custom_properties,
-      std::unique_ptr<Vector<CSSSyntaxDescriptor>> input_argument_types,
+      Vector<CSSSyntaxDescriptor> input_argument_types,
       double alpha);
   typedef HashMap<String, std::unique_ptr<MainThreadDocumentPaintDefinition>>
       MainThreadDocumentDefinitionMap;
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
index 347872f..9ab00290 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
@@ -226,33 +226,8 @@
   } else {
     PaintWorklet* paint_worklet =
         PaintWorklet::From(*GetFrame()->GetDocument()->domWindow());
-    PaintWorklet::DocumentDefinitionMap& document_definition_map =
-        paint_worklet->GetDocumentDefinitionMap();
-    if (document_definition_map.Contains(name)) {
-      DocumentPaintDefinition* existing_document_definition =
-          document_definition_map.at(name);
-      if (existing_document_definition == kInvalidDocumentPaintDefinition)
-        return;
-      if (!existing_document_definition->RegisterAdditionalPaintDefinition(
-              *definition)) {
-        document_definition_map.Set(name, kInvalidDocumentPaintDefinition);
-        exception_state.ThrowDOMException(
-            DOMExceptionCode::kNotSupportedError,
-            "A class with name:'" + name +
-                "' was registered with a different definition.");
-        return;
-      }
-      // Notify the generator ready only when register paint is called the
-      // second time with the same |name| (i.e. there is already a document
-      // definition associated with |name|
-      if (existing_document_definition->GetRegisteredDefinitionCount() ==
-          PaintWorklet::kNumGlobalScopes)
-        pending_generator_registry_->NotifyGeneratorReady(name);
-    } else {
-      DocumentPaintDefinition* document_definition =
-          MakeGarbageCollected<DocumentPaintDefinition>(definition);
-      document_definition_map.Set(name, document_definition);
-    }
+    paint_worklet->RegisterCSSPaintDefinition(name, definition,
+                                              exception_state);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
index 0a79961..4967e99 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
@@ -112,17 +112,13 @@
     for (const auto& property : custom_properties)
       passed_custom_properties.push_back(property.GetString());
 
-    Vector<CSSSyntaxDescriptor> passed_input_argument_types;
-    for (const auto& syntax_descriptor : definition->InputArgumentTypes())
-      passed_input_argument_types.push_back(syntax_descriptor.IsolatedCopy());
     PostCrossThreadTask(
         *main_thread_runner_, FROM_HERE,
         CrossThreadBindOnce(
             &PaintWorklet::RegisterMainThreadDocumentPaintDefinition,
             paint_worklet_, name, definition->NativeInvalidationProperties(),
             WTF::Passed(std::move(passed_custom_properties)),
-            std::make_unique<Vector<CSSSyntaxDescriptor>>(
-                passed_input_argument_types),
+            definition->InputArgumentTypes(),
             definition->GetPaintRenderingContext2DSettings()->alpha()));
   }
 }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
index 491cc5b..ed5cb1c 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
@@ -190,7 +190,7 @@
   TestPaintWorklet* paint_worklet_to_test = GetTestPaintWorklet();
   paint_worklet_to_test->RegisterMainThreadDocumentPaintDefinition(
       "foo", native_invalidation_properties, custom_invalidation_properties,
-      std::make_unique<Vector<CSSSyntaxDescriptor>>(), true);
+      Vector<CSSSyntaxDescriptor>(), true);
 
   CSSPaintImageGeneratorImpl* generator =
       MakeGarbageCollected<CSSPaintImageGeneratorImpl>(paint_worklet_to_test,
diff --git a/third_party/blink/renderer/modules/wake_lock/DEPS b/third_party/blink/renderer/modules/wake_lock/DEPS
index 665d622e..2cfae9a7 100644
--- a/third_party/blink/renderer/modules/wake_lock/DEPS
+++ b/third_party/blink/renderer/modules/wake_lock/DEPS
@@ -3,7 +3,6 @@
   "+services/device/public/mojom",
   "+services/device/public/interfaces/constants.mojom-blink.h",
   "+services/device/public/mojom/wake_lock.mojom-blink.h",
-  "+third_party/blink/renderer/modules/event_target_modules.h",
 ]
 
 specific_include_rules = {
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
index a429a23..c060e4a 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
@@ -66,7 +66,7 @@
   // 2. Reject lockPromise with an "AbortError" DOMException.
   ScriptPromiseResolver* resolver = *iterator;
   resolver->Reject(MakeGarbageCollected<DOMException>(
-      DOMExceptionCode::kAbortError, "Wake Lock is being released"));
+      DOMExceptionCode::kAbortError, "Wake Lock released"));
 
   // 3. Let document be the responsible document of the current settings object.
   // 4. Let record be the platform wake lock's state record associated with
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index dfd7b1d9f..99c5670 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -294,6 +294,25 @@
 crbug.com/836884 virtual/disable-blink-gen-property-trees/compositing/overflow/composited-scroll-with-fractional-translation.html [ Failure ]
 crbug.com/836884 virtual/disable-blink-gen-property-trees/compositing/video/video-controls-layer-creation-squashing.html [ Failure ]
 
+# These fail when device_scale_factor is changed, but only for anti-aliasing:
+crbug.com/968791 virtual/scalefactor200/css3/filters/effect-blur-hw.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/css3/filters/filterRegions.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/css-filters-animation-blur.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/css-filters-animation-combined-001.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filter-subregion-01.html [ Failure ]
+
+# These appear to be actually incorrect at device_scale_factor 2.0:
+crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-image-hw.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-image-lazy-attach.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-image.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filtered-inline-applies-to-float.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filters-test-brightness-003.html [ Failure ]
+
+# These seem flaky on scalefactor200:
+crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-hw.html [ Pass Failure Timeout ]
+crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-subregion-hw.html [ Pass Failure Timeout ]
+crbug.com/968791 virtual/scalefactor200/css3/filters/effect-contrast-hw.html [ Pass Failure Timeout ]
+
 # ==== Regressions introduced by BlinkGenPropertyTrees =====
 # Reflection / mask ordering issue
 crbug.com/767318 compositing/reflections/nested-reflection-mask-change.html [ Failure ]
@@ -1659,6 +1678,7 @@
 crbug.com/538697 [ Win ] printing/webgl-oversized-printing.html [ Failure Crash ]
 
 crbug.com/904592 css3/filters/backdrop-filter-svg.html [ Failure ]
+crbug.com/904592 virtual/scalefactor200/css3/filters/backdrop-filter-svg.html [ Failure ]
 
 crbug.com/492664 [ Linux ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vlr-005.xht [ Failure ]
 crbug.com/492664 [ Linux ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vrl-004.xht [ Failure ]
@@ -5845,3 +5865,6 @@
 
 # Sheriff 2019-06-04
 crbug.com/970135 [ Mac ] virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-interested-element-indicated.html [ Failure ]
+crbug.com/970334 [ Mac ] fast/spatial-navigation/snav-tiny-table-traversal.html [ Failure ]
+crbug.com/970142 http/tests/security/mixedContent/insecure-css-resources.html [ Failure ]
+crbug.com/970142 virtual/blink-cors/http/tests/security/mixedContent/insecure-css-resources.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index a1e6ca1..a8c2f87 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -328,6 +328,16 @@
     "args": ["--force-device-scale-factor=2"]
   },
   {
+    "prefix": "scalefactor200",
+    "base": "css3/filters",
+    "args": ["--force-device-scale-factor=2"]
+  },
+  {
+    "prefix": "scalefactor200",
+    "base": "external/wpt/css/filter-effects",
+    "args": ["--force-device-scale-factor=2"]
+  },
+  {
     "prefix": "scalefactor150",
     "base": "fast/hidpi/static",
     "args": ["--force-device-scale-factor=1.5"]
diff --git a/third_party/blink/web_tests/WebDriverExpectations b/third_party/blink/web_tests/WebDriverExpectations
index 528727a..9acec1f 100644
--- a/third_party/blink/web_tests/WebDriverExpectations
+++ b/third_party/blink/web_tests/WebDriverExpectations
@@ -52,6 +52,7 @@
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_dismiss[capabilities0-alert] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state[realmSetting0-denied] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/user_prompts.py>>test_dismiss[capabilities0-alert-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_promise_reject_timeout [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/maximize_window/maximize.py>>test_maximize_when_resized_to_max_size [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/json_serialize_windowproxy.py>>test_window_open [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state_cross_realm[realmSetting0-denied] [ Failure ]
@@ -60,6 +61,7 @@
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_default[prompt] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state[realmSetting0-prompt] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_ignore[capabilities0-alert] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_promise_resolve_timeout [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/perform_actions/key_events.py>>test_modifier_key_sends_correct_events[\ue03d-META] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt] [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-letter-crash.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-letter-crash.html
new file mode 100644
index 0000000..683d2a7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-letter-crash.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/967194">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#target:first-letter{
+  float:right;
+}
+</style>
+The test passes if it does not CRASH.
+<div id="target">
+  <textarea style="display: block;">A</textarea>
+</div>
+<script>test(() => { });</script>
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/active-processing.https.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/active-processing.https.html
new file mode 100644
index 0000000..0fa3089a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/active-processing.https.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<html>
+  <head>
+    <title>
+      Test Active Processing for AudioBufferSourceNode
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+  </head>
+
+  <body>
+    <script id="layout-test-code">
+      let audit = Audit.createTaskRunner();
+
+      // Arbitrary sample rate. And we only new a few blocks for rendering to
+      // see if things are working.
+      let sampleRate = 8000;
+      let renderLength = 10 * RENDER_QUANTUM_FRAMES;
+
+      // Offline context used for the tests.
+      let context;
+
+      // Number of channels for the AudioBufferSource. Fairly arbitrary, but
+      // should be more than 2.
+      let numberOfChannels = 7;
+
+      // Number of frames in the AudioBuffer.  Fairly arbitrary, but should
+      // probablybe more than one render quantum and significantly less than
+      // |renderLength|.
+      let bufferFrames = 131;
+
+      let filePath =
+          '../the-audioworklet-interface/processors/input-count-processor.js';
+
+      audit.define('Setup graph', (task, should) => {
+        context =
+            new OfflineAudioContext(numberOfChannels, renderLength, sampleRate);
+
+        should(
+            context.audioWorklet.addModule(filePath).then(() => {
+              let buffer = new AudioBuffer({
+                numberOfChannels: numberOfChannels,
+                length: bufferFrames,
+                sampleRate: context.sampleRate
+              });
+
+              src = new AudioBufferSourceNode(context, {buffer: buffer});
+              let counter = new AudioWorkletNode(context, 'counter');
+
+              src.connect(counter).connect(context.destination);
+              src.start();
+            }),
+            'AudioWorklet and graph construction')
+            .beResolved()
+            .then(() => task.done());
+      });
+
+      audit.define('verify count change', (task, should) => {
+        context.startRendering()
+            .then(renderedBuffer => {
+              let output = renderedBuffer.getChannelData(0);
+
+              // Find the first time the number of channels changes to 1.
+              let countChangeIndex = output.findIndex(x => x == 1);
+
+              // Verify that the count did change.  If it didn't there's a bug
+              // in the imploementation, or it takes longer than the render
+              // length to change.  for the latter case, increase the render
+              // length, but it can't be arbitrarily large.  The change needs to
+              // happen at some reasonable time after the source stops.
+              should(countChangeIndex >= 0, 'Number of channels changed')
+                  .beTrue();
+              should(
+                  countChangeIndex, 'Index where input channel count changed')
+                  .beLessThanOrEqualTo(renderLength);
+
+              // Verify the number of channels at the beginning matches the
+              // number of channels in the AudioBuffer.
+              should(
+                  output.slice(0, countChangeIndex),
+                  `Number of channels in input[0:${countChangeIndex - 1}]`)
+                  .beConstantValueOf(numberOfChannels);
+
+              // Verify that after the source has stopped, the number of
+              // channels is 1.
+              should(
+                  output.slice(countChangeIndex),
+                  `Number of channels in input[${countChangeIndex}:]`)
+                  .beConstantValueOf(1);
+            })
+            .then(() => task.done());
+      });
+
+      audit.run();
+    </script>
+
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/input-count-processor.js b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/input-count-processor.js
new file mode 100644
index 0000000..6d53ba8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/input-count-processor.js
@@ -0,0 +1,22 @@
+/**
+ * @class CountProcessor
+ * @extends AudioWorkletProcessor
+ *
+ * This processor class just looks at the number of input channels on the first
+ * input and fills the first output channel with that value.
+ */
+class CountProcessor extends AudioWorkletProcessor {
+  constructor() {
+    super();
+  }
+
+  process(inputs, outputs, parameters) {
+    let input = inputs[0];
+    let output = outputs[0];
+    output[0].fill(input.length);
+
+    return true;
+  }
+}
+
+registerProcessor('counter', CountProcessor);
diff --git a/third_party/blink/web_tests/http/tests/contacts/resources/non-main-frame-select.html b/third_party/blink/web_tests/http/tests/contacts/resources/non-main-frame-select.html
index c75186909..094256d 100644
--- a/third_party/blink/web_tests/http/tests/contacts/resources/non-main-frame-select.html
+++ b/third_party/blink/web_tests/http/tests/contacts/resources/non-main-frame-select.html
@@ -2,13 +2,8 @@
 'use strict';
 
 window.onload = function() {
-  navigator.contacts.select({
-    multiple: "true",
-    properties: ['name', 'email']
-  }).then(results => {
-    parent.postMessage({ errorMsg: '' }, '*');
-  }).catch(exception => {
-    parent.postMessage({ errorMsg: exception.toString() }, '*');
-  });
+  navigator.contacts.select(['name', 'email'], { multiple: true })
+    .then(results => parent.postMessage({ errorMsg: '' }, '*'))
+    .catch(exception => parent.postMessage({ errorMsg: exception.toString() }, '*'));
 }
 </script>
diff --git a/third_party/blink/web_tests/http/tests/contacts/select-function.html b/third_party/blink/web_tests/http/tests/contacts/select-function.html
index 8a85615..1f1a47043 100644
--- a/third_party/blink/web_tests/http/tests/contacts/select-function.html
+++ b/third_party/blink/web_tests/http/tests/contacts/select-function.html
@@ -35,7 +35,7 @@
 
 promise_test(async () => {
   await expectTypeError(() =>
-      navigator.contacts.select({ properties: ['name'] }));
+      navigator.contacts.select(['name']));
 
 }, 'The Contact API requires a user gesture')
 
@@ -44,17 +44,17 @@
 
   // At least one property must be provided.
   await expectTypeError(() => navigator.contacts.select());
-  await expectTypeError(() => navigator.contacts.select({ properties: [] }));
+  await expectTypeError(() => navigator.contacts.select([]));
 
   // Per WebIDL parsing, no invalid values may be provided.
   await expectTypeError(() =>
-      navigator.contacts.select({ properties: [''] }));
+      navigator.contacts.select(['']));
   await expectTypeError(() =>
-      navigator.contacts.select({ properties: ['foo'] }));
+      navigator.contacts.select(['foo']));
   await expectTypeError(() =>
-      navigator.contacts.select({ properties: ['name', 'photo'] }));
+      navigator.contacts.select(['name', 'photo']));
 
-}, 'The Contact API requires at least one valid property to be provided');
+}, 'The Contact API requires valid properties to be provided');
 
 promise_test(async () => {
   triggerUserGesture();
@@ -64,8 +64,7 @@
     return { contacts: null };
   });
 
-  await expectTypeError(() =>
-      navigator.contacts.select({ properties: ['name'] }));
+  await expectTypeError(() => navigator.contacts.select(['name']));
 
 }, 'The Contact API can fail when the selector cannot be opened');
 
@@ -83,10 +82,7 @@
   });
 
   await expectTypeError(() => {
-    return navigator.contacts.select({
-        properties: ['name', 'email'],
-        multiple: true
-    });
+    return navigator.contacts.select(['name', 'email'], { multiple: true });
   });
 
   assert_not_equals(storedOptions, null);
@@ -110,10 +106,7 @@
     };
   });
 
-  const results = await navigator.contacts.select({
-    properties: ['name', 'email', 'tel'],
-    multiple: true
-  });
+  const results = await navigator.contacts.select(['name', 'email', 'tel'], { multiple: true });
 
   assert_equals(results.length, 2);
 
@@ -153,9 +146,7 @@
     };
   });
 
-  const results = await navigator.contacts.select({
-    properties: ['name']
-  });
+  const results = await navigator.contacts.select(['name']);
 
   assert_equals(results.length, 1);
 
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..3f18468f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..5382648
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [36, 36],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [36, 264],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [36, 492],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [36, 720],
+      "bounds": [320, 180]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..9c7b30f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1570, 1170],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1712, 1422],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..b6d88a18
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1570, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1570, 1212],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..b6d88a18
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1570, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1570, 1212],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png
new file mode 100644
index 0000000..623c41f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
new file mode 100644
index 0000000..fee4115
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-expected.png
new file mode 100644
index 0000000..60c2121
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
new file mode 100644
index 0000000..a70067f4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-expected.png
new file mode 100644
index 0000000..487badf5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
new file mode 100644
index 0000000..86f4a55
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-expected.png
new file mode 100644
index 0000000..a54bec4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..4c9204b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png
new file mode 100644
index 0000000..3ee4f62
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
new file mode 100644
index 0000000..0e5bc0f3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png
new file mode 100644
index 0000000..007fec52
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png
new file mode 100644
index 0000000..dc130c700
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png
new file mode 100644
index 0000000..5d44c7f3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
new file mode 100644
index 0000000..9980dd3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-expected.png
new file mode 100644
index 0000000..a8a07a2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
new file mode 100644
index 0000000..a8a07a2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-expected.png
new file mode 100644
index 0000000..a2b3e735
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
new file mode 100644
index 0000000..a2b3e735
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..2efc3e4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..393138e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png
new file mode 100644
index 0000000..bdc645b4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
new file mode 100644
index 0000000..9f33297
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..ee72c0a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..0065caa8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png
new file mode 100644
index 0000000..7e63a2b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
new file mode 100644
index 0000000..df68f6bb2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..eb0f3a3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..6f153ab7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-expected.png
new file mode 100644
index 0000000..c264fe5b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
new file mode 100644
index 0000000..0b4d6195
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-expected.png
new file mode 100644
index 0000000..66cb7b23
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
new file mode 100644
index 0000000..72ae4a00
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png
new file mode 100644
index 0000000..1538bd2a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png
new file mode 100644
index 0000000..1a60235
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/http/tests/security/cookies/basic-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/http/tests/security/cookies/basic-expected.txt
new file mode 100644
index 0000000..2de19173
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/http/tests/security/cookies/basic-expected.txt
@@ -0,0 +1,4 @@
+CONSOLE WARNING: [Deprecation] A cookie associated with a cross-site resource at http://localhost:8000/security/cookies/resources/set-a-cookie.html was set without the `SameSite` attribute. Starting in M77, Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592.
+Running test.
+secret=PASS
+Test complete.
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..3f18468f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
new file mode 100644
index 0000000..3b4a8ecb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
new file mode 100644
index 0000000..79f4913
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..a65eaa8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [18, 18],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [18, 132],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [18, 246],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [18, 360],
+      "bounds": [160, 90]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
new file mode 100644
index 0000000..d9fa6bcd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
new file mode 100644
index 0000000..700335c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..40d10fc5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 585],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [835, 711],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..46b61b2c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..32ed34b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..54d7015
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..c2c56ea6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..7c6d7e50
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..bb90499
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..10d9bca4e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
new file mode 100644
index 0000000..5853476
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
new file mode 100644
index 0000000..ec40383
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
new file mode 100644
index 0000000..8b4ed3f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='filtered box'",
+      "position": [8, 8],
+      "bounds": [144, 144]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='compositing box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [30, 30, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 1, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
new file mode 100644
index 0000000..58ba65a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/http/tests/security/cookies/basic-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/http/tests/security/cookies/basic-expected.txt
new file mode 100644
index 0000000..b38b7e4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/http/tests/security/cookies/basic-expected.txt
@@ -0,0 +1,3 @@
+Running test.
+secret=PASS
+Test complete.
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..3f18468f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
new file mode 100644
index 0000000..3b4a8ecb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
new file mode 100644
index 0000000..79f4913
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..a65eaa8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [18, 18],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [18, 132],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [18, 246],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [18, 360],
+      "bounds": [160, 90]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
new file mode 100644
index 0000000..d9fa6bcd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
new file mode 100644
index 0000000..700335c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..40d10fc5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 585],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [835, 711],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..46b61b2c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..32ed34b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..54d7015
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..c2c56ea6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..7c6d7e50
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
new file mode 100644
index 0000000..0e4a982
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..bb90499
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..10d9bca4e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
new file mode 100644
index 0000000..5853476
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
new file mode 100644
index 0000000..ec40383
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
new file mode 100644
index 0000000..8b4ed3f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='filtered box'",
+      "position": [8, 8],
+      "bounds": [144, 144]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='compositing box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [30, 30, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 1, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
new file mode 100644
index 0000000..58ba65a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..3f18468f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
new file mode 100644
index 0000000..3b4a8ecb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
new file mode 100644
index 0000000..79f4913
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..a65eaa8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [18, 18],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [18, 132],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [18, 246],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [18, 360],
+      "bounds": [160, 90]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
new file mode 100644
index 0000000..d9fa6bcd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
new file mode 100644
index 0000000..700335c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..40d10fc5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 585],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [835, 711],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..46b61b2c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..32ed34b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..54d7015
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..c2c56ea6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..bb90499
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..10d9bca4e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
new file mode 100644
index 0000000..5853476
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
new file mode 100644
index 0000000..ec40383
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
new file mode 100644
index 0000000..8b4ed3f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='filtered box'",
+      "position": [8, 8],
+      "bounds": [144, 144]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='compositing box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [30, 30, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 1, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
new file mode 100644
index 0000000..58ba65a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..3f18468f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
new file mode 100644
index 0000000..3b4a8ecb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
new file mode 100644
index 0000000..79f4913
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..a65eaa8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [18, 18],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [18, 132],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [18, 246],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [18, 360],
+      "bounds": [160, 90]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
new file mode 100644
index 0000000..d9fa6bcd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
new file mode 100644
index 0000000..700335c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..40d10fc5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 585],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [835, 711],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..46b61b2c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..32ed34b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..54d7015
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..c2c56ea6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
new file mode 100644
index 0000000..0e4a982
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..bb90499
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..10d9bca4e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
new file mode 100644
index 0000000..5853476
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
new file mode 100644
index 0000000..ec40383
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
new file mode 100644
index 0000000..8b4ed3f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='filtered box'",
+      "position": [8, 8],
+      "bounds": [144, 144]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='compositing box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [30, 30, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 1, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
new file mode 100644
index 0000000..58ba65a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..3f18468f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
new file mode 100644
index 0000000..f0aa6cb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
new file mode 100644
index 0000000..3b4a8ecb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
new file mode 100644
index 0000000..79f4913
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
new file mode 100644
index 0000000..9d425786
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..a65eaa8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [18, 18],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [18, 132],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [18, 246],
+      "bounds": [160, 90]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [18, 360],
+      "bounds": [160, 90]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
new file mode 100644
index 0000000..d9fa6bcd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
new file mode 100644
index 0000000..700335c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..40d10fc5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 585],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [835, 711],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..ddac1152
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [785, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [785, 606],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [100, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..46b61b2c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..32ed34b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..54d7015
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..c2c56ea6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png
new file mode 100644
index 0000000..16ac3e8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.txt
new file mode 100644
index 0000000..5a6ac0b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.txt
@@ -0,0 +1,2 @@
+Content-Type: text/plain
+#EOF
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png
new file mode 100644
index 0000000..82e11f8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
new file mode 100644
index 0000000..d4fd81c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..bb90499
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..10d9bca4e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
new file mode 100644
index 0000000..5853476
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png
new file mode 100644
index 0000000..a8cd675
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png
new file mode 100644
index 0000000..1ca60cb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
new file mode 100644
index 0000000..ec40383
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
new file mode 100644
index 0000000..8b4ed3f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='filtered box'",
+      "position": [8, 8],
+      "bounds": [144, 144]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='compositing box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [30, 30, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 1, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/nested-filter-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/nested-filter-expected.png
new file mode 100644
index 0000000..036c269
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/nested-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
new file mode 100644
index 0000000..58ba65a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..f76fe3d9
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..34f3e02e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [36, 36],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [36, 265],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [36, 494],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [36, 723],
+      "bounds": [320, 180]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..b434669d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1570, 1170],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1712, 1376],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..13af038d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..13af038d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png
new file mode 100644
index 0000000..e026b36
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
new file mode 100644
index 0000000..2813b3e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-expected.png
new file mode 100644
index 0000000..6a8debe
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
new file mode 100644
index 0000000..0617020
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-expected.png
new file mode 100644
index 0000000..c7fc81f5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
new file mode 100644
index 0000000..81c272b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-expected.png
new file mode 100644
index 0000000..6d450f8a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..9f2b1af
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png
new file mode 100644
index 0000000..26aa89f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
new file mode 100644
index 0000000..9cf56519
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png
new file mode 100644
index 0000000..c61efde
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png
new file mode 100644
index 0000000..1deb82bd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png
new file mode 100644
index 0000000..829abc1f5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
new file mode 100644
index 0000000..ec8e7a77
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-expected.png
new file mode 100644
index 0000000..30a1924
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
new file mode 100644
index 0000000..30a1924
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-expected.png
new file mode 100644
index 0000000..7395978
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
new file mode 100644
index 0000000..7395978
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..e5dd6bf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..1cd8126
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png
new file mode 100644
index 0000000..e7806794
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
new file mode 100644
index 0000000..ebb4d76
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..0fd1ee936
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..27727867
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png
new file mode 100644
index 0000000..637bdd4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
new file mode 100644
index 0000000..97c30c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..d43dc6d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..0b49854f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-expected.png
new file mode 100644
index 0000000..002920c3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
new file mode 100644
index 0000000..4711bb1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-expected.png
new file mode 100644
index 0000000..4b3cde0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
new file mode 100644
index 0000000..61850c7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png
new file mode 100644
index 0000000..4adde80
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png
new file mode 100644
index 0000000..5de3687a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..f76fe3d9
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..34f3e02e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [36, 36],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [36, 265],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [36, 494],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [36, 723],
+      "bounds": [320, 180]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..b434669d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1570, 1170],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1712, 1376],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..13af038d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..13af038d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png
new file mode 100644
index 0000000..e026b36
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
new file mode 100644
index 0000000..2813b3e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-expected.png
new file mode 100644
index 0000000..6a8debe
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
new file mode 100644
index 0000000..0617020
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-expected.png
new file mode 100644
index 0000000..c7fc81f5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
new file mode 100644
index 0000000..81c272b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-expected.png
new file mode 100644
index 0000000..6d450f8a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..9f2b1af
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png
new file mode 100644
index 0000000..26aa89f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
new file mode 100644
index 0000000..9cf56519
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png
new file mode 100644
index 0000000..c61efde
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png
new file mode 100644
index 0000000..829abc1f5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
new file mode 100644
index 0000000..ec8e7a77
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
new file mode 100644
index 0000000..30a1924
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-opacity-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-opacity-expected.png
new file mode 100644
index 0000000..7395978
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..e5dd6bf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..1cd8126
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png
new file mode 100644
index 0000000..e7806794
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
new file mode 100644
index 0000000..ebb4d76
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..0fd1ee936
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..27727867
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..d43dc6d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..0b49854f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
new file mode 100644
index 0000000..4711bb1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-expected.png
new file mode 100644
index 0000000..4b3cde0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
new file mode 100644
index 0000000..61850c7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/README.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/README.txt
new file mode 100644
index 0000000..530ac2d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/README.txt
@@ -0,0 +1,2 @@
+# This suite runs the tests in fast/hidpi/static with
+# --force-device-scale-factor=2
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/add-filter-rendering-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/add-filter-rendering-expected.png
new file mode 100644
index 0000000..4d5e090
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/add-filter-rendering-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-basic-blur-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-basic-blur-expected.png
new file mode 100644
index 0000000..17a95872
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-basic-blur-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-border-radius-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-border-radius-expected.png
new file mode 100644
index 0000000..f0ae23f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
new file mode 100644
index 0000000..557b0fc
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
new file mode 100644
index 0000000..4b960016
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
new file mode 100644
index 0000000..5afe8db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-plus-filter-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-plus-filter-expected.png
new file mode 100644
index 0000000..141896f39
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-plus-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-svg-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-svg-expected.png
new file mode 100644
index 0000000..66e56c8
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
new file mode 100644
index 0000000..23174df
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-expected.png
new file mode 100644
index 0000000..b372409d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-parents-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-parents-expected.png
new file mode 100644
index 0000000..652f574
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-parents-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png
new file mode 100644
index 0000000..adc09f2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/buffer-offset-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/buffer-offset-expected.png
new file mode 100644
index 0000000..9e969ac
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/buffer-offset-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
new file mode 100644
index 0000000..5382648
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@
+  
+  
+  
+  
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutImage IMG id='grayscale-box'",
+      "position": [36, 36],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='saturate-box'",
+      "position": [36, 264],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='invert-box'",
+      "position": [36, 492],
+      "bounds": [320, 180]
+    },
+    {
+      "name": "LayoutImage IMG id='brightness-box'",
+      "position": [36, 720],
+      "bounds": [320, 180]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
new file mode 100644
index 0000000..b7b42ad
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
new file mode 100644
index 0000000..6a67b2b8
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
new file mode 100644
index 0000000..9c7b30f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1570, 1170],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1712, 1422],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 200, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
new file mode 100644
index 0000000..b6d88a18
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1570, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1570, 1212],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
new file mode 100644
index 0000000..b6d88a18
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1570, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1570, 1212],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
+      "position": [200, 200],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#000000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [500, 500, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-reflected-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-reflected-expected.png
new file mode 100644
index 0000000..a926cc9
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-reflected-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/crash-filter-change-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/crash-filter-change-expected.png
new file mode 100644
index 0000000..c69594c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/crash-filter-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/css-opacity-with-drop-shadow-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/css-opacity-with-drop-shadow-expected.png
new file mode 100644
index 0000000..5cd9b2b7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/css-opacity-with-drop-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
new file mode 100644
index 0000000..799831e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-expected.png
new file mode 100644
index 0000000..afa83a6
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png
new file mode 100644
index 0000000..4d3a707
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png
new file mode 100644
index 0000000..623c41f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
new file mode 100644
index 0000000..fee4115
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-expected.png
new file mode 100644
index 0000000..60c2121
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
new file mode 100644
index 0000000..a70067f4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-expected.png
new file mode 100644
index 0000000..487badf5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
new file mode 100644
index 0000000..86f4a55
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-expected.png
new file mode 100644
index 0000000..a54bec4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
new file mode 100644
index 0000000..4c9204b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png
new file mode 100644
index 0000000..3ee4f62
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
new file mode 100644
index 0000000..0e5bc0f3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png
new file mode 100644
index 0000000..007fec52
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png
new file mode 100644
index 0000000..dc130c700
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png
new file mode 100644
index 0000000..5d44c7f3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
new file mode 100644
index 0000000..9980dd3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-expected.png
new file mode 100644
index 0000000..a8a07a2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
new file mode 100644
index 0000000..a8a07a2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-expected.png
new file mode 100644
index 0000000..a2b3e735
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
new file mode 100644
index 0000000..a2b3e735
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
new file mode 100644
index 0000000..2efc3e4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
new file mode 100644
index 0000000..393138e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png
new file mode 100644
index 0000000..bdc645b4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
new file mode 100644
index 0000000..9f33297
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-expected.png
new file mode 100644
index 0000000..ee72c0a
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-external-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-external-expected.png
new file mode 100644
index 0000000..1e7342c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-external-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-expected.png
new file mode 100644
index 0000000..3b143b7c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png
new file mode 100644
index 0000000..18f04655
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
new file mode 100644
index 0000000..0065caa8
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-expected.png
new file mode 100644
index 0000000..255d8a7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
new file mode 100644
index 0000000..7895189
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-expected.png
new file mode 100644
index 0000000..48d39c2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png
new file mode 100644
index 0000000..806e098
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-colormatrix-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-colormatrix-expected.png
new file mode 100644
index 0000000..22c1d6e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-colormatrix-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png
new file mode 100644
index 0000000..2d3d74f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
new file mode 100644
index 0000000..2ec460b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
new file mode 100644
index 0000000..2ec460b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
new file mode 100644
index 0000000..eb0f3a3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..6f153ab7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-expected.png
new file mode 100644
index 0000000..c264fe5b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
new file mode 100644
index 0000000..0b4d6195
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-expected.png
new file mode 100644
index 0000000..66cb7b23
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
new file mode 100644
index 0000000..72ae4a00
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png
new file mode 100644
index 0000000..fdeb602
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png
new file mode 100644
index 0000000..0ce12565
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-region-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-region-expected.png
new file mode 100644
index 0000000..ccc9290
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-region-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-blur-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-blur-expected.png
new file mode 100644
index 0000000..5dcd73a
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-blur-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
new file mode 100644
index 0000000..98eacb9
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png
new file mode 100644
index 0000000..659a5071
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png
new file mode 100644
index 0000000..659a5071
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-expected.png
new file mode 100644
index 0000000..a6fb29f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-sepia-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-sepia-expected.png
new file mode 100644
index 0000000..96e4dd5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-sepia-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-clipped-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-clipped-expected.png
new file mode 100644
index 0000000..15d1479
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-clipped-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-expected.png
new file mode 100644
index 0000000..503fa3a3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-rotated-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-rotated-expected.png
new file mode 100644
index 0000000..f415621
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-rotated-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
new file mode 100644
index 0000000..33924a2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.png
new file mode 100644
index 0000000..96179a8
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
new file mode 100644
index 0000000..e371a90b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='filtered box'",
+      "position": [16, 16],
+      "bounds": [288, 288]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='compositing box'",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 2, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-inline-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-inline-expected.png
new file mode 100644
index 0000000..d3ef6ce
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-inline-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/multiple-filters-invalidation-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/multiple-filters-invalidation-expected.png
new file mode 100644
index 0000000..486cbd86
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/multiple-filters-invalidation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filter-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filter-expected.png
new file mode 100644
index 0000000..9d781f08
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filters-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filters-expected.png
new file mode 100644
index 0000000..6ca6e583
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filters-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/regions-expanding-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/regions-expanding-expected.png
new file mode 100644
index 0000000..9ad2a23
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/regions-expanding-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/remove-filter-rendering-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/remove-filter-rendering-expected.png
new file mode 100644
index 0000000..df48b71
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/remove-filter-rendering-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
new file mode 100644
index 0000000..45bbaad
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1600, 1200],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [1600, 1200],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [1600, 1200],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/simple-filter-rendering-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/simple-filter-rendering-expected.png
new file mode 100644
index 0000000..4d5e090
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/simple-filter-rendering-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/external/wpt/css/filter-effects/README.txt b/third_party/blink/web_tests/virtual/scalefactor200/external/wpt/css/filter-effects/README.txt
new file mode 100644
index 0000000..530ac2d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scalefactor200/external/wpt/css/filter-effects/README.txt
@@ -0,0 +1,2 @@
+# This suite runs the tests in fast/hidpi/static with
+# --force-device-scale-factor=2
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 9f58d36..1e4046d 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 249879928
-Date: 2019/05/24 UTC
+Version: 250946462
+Date: 2019/05/31 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/trace_log.proto b/third_party/metrics_proto/trace_log.proto
index 9917eb7..e0247f4 100644
--- a/third_party/metrics_proto/trace_log.proto
+++ b/third_party/metrics_proto/trace_log.proto
@@ -21,4 +21,53 @@
 message TraceLog {
   // Client uploads the trace data as a byte buffer.
   optional bytes raw_data = 1;
+
+  // Metadata related to the trace log, like trigger for the upload, etc.
+  optional TraceMetadata metadata = 3;
+}
+
+// This field contains metadata associated with the trace.
+message TraceMetadata {
+  // Information about a trigger rule defined in the experiment config.
+  message TriggerRule {
+    enum TriggerType {
+      UNKNOWN = 0;
+
+      // Traces are triggered by specific range of values of an UMA histogram.
+      MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE = 1;
+
+      // Traces are triggered by specific named events in chromium codebase,
+      // like "second-update-failure".
+      MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED = 2;
+    }
+    optional TriggerType trigger_type = 1;
+
+    // Configuration of histogram trigger.
+    message HistogramRule {
+      // UMA histogram name hash, same as HistogramEventProto.name_hash.
+      optional fixed64 histogram_name_hash = 1;
+
+      // Range of values of the histogram that activates trigger.
+      optional int64 histogram_min_trigger = 2;
+      optional int64 histogram_max_trigger = 3;
+    }
+    optional HistogramRule histogram_rule = 2;
+
+    // Configuration of named trigger.
+    message NamedRule {
+      enum EventType {
+        INVALID = 0;
+        SESSION_RESTORE = 1;
+        NAVIGATION = 2;
+      }
+      optional EventType event_type = 1;
+    }
+    optional NamedRule named_rule = 3;
+  }
+
+  // Specifies the rule that caused the trace to be uploaded.
+  optional TriggerRule triggered_rule = 1;
+
+  // List of all active triggers in current session, when trace was triggered.
+  repeated TriggerRule active_rules = 2;
 }
diff --git a/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js b/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
index 93b9e37d..7d8d859 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
@@ -8,10 +8,10 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /**
 `iron-a11y-announcer` is a singleton element that is intended to add a11y
diff --git a/third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js
index dae57c4..84eb182 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 /**
  * Chrome uses an older version of DOM Level 3 Keyboard Events
diff --git a/third_party/polymer/v3_0/components-chromium/iron-a11y-keys/iron-a11y-keys.js b/third_party/polymer/v3_0/components-chromium/iron-a11y-keys/iron-a11y-keys.js
index a84490a2..c974bb6 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-a11y-keys/iron-a11y-keys.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-a11y-keys/iron-a11y-keys.js
@@ -8,10 +8,10 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
 /**
 `iron-a11y-keys` provides a cross-browser interface for processing
 keyboard commands. The interface adheres to [WAI-ARIA best
diff --git a/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-button-state.js b/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-button-state.js
index a163674..ca9a41e7 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-button-state.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-button-state.js
@@ -8,11 +8,11 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import './iron-control-state.js';
 
 import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 
 /**
  * @demo demo/index.html
diff --git a/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-control-state.js b/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-control-state.js
index 0d7d3a1e..15b4cbb8 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-control-state.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-control-state.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 
 /**
  * @demo demo/index.html
diff --git a/third_party/polymer/v3_0/components-chromium/iron-collapse/iron-collapse.js b/third_party/polymer/v3_0/components-chromium/iron-collapse/iron-collapse.js
index 75372db..f6d3af14 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-collapse/iron-collapse.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-collapse/iron-collapse.js
@@ -9,10 +9,10 @@
 found at http://polymer.github.io/PATENTS.txt
 */
 import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
-import {Base} from '../polymer/polymer-legacy.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
+import {Base} from '../polymer/polymer_bundled.min.js';
 
 /**
 `iron-collapse` creates a collapsible block of content.  By default, the content
diff --git a/third_party/polymer/v3_0/components-chromium/iron-dropdown/iron-dropdown.js b/third_party/polymer/v3_0/components-chromium/iron-dropdown/iron-dropdown.js
index 288e624..83bef0e 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-dropdown/iron-dropdown.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-dropdown/iron-dropdown.js
@@ -8,15 +8,15 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
 import {IronControlState} from '../iron-behaviors/iron-control-state.js';
 import {IronOverlayBehavior, IronOverlayBehaviorImpl} from '../iron-overlay-behavior/iron-overlay-behavior.js';
 import {NeonAnimationRunnerBehavior} from '../neon-animation/neon-animation-runner-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /**
 `<iron-dropdown>` is a generalized element that is useful when you have
diff --git a/third_party/polymer/v3_0/components-chromium/iron-fit-behavior/iron-fit-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-fit-behavior/iron-fit-behavior.js
index 48d9b70f1..b8cae11 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-fit-behavior/iron-fit-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-fit-behavior/iron-fit-behavior.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 
 /**
 `Polymer.IronFitBehavior` fits an element in another element using `max-height`
diff --git a/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout-classes.js b/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout-classes.js
index 54edcb4c..3e49e59 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout-classes.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout-classes.js
@@ -8,8 +8,8 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /*
 A set of layout classes that let you specify layout properties directly in
diff --git a/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout.js b/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout.js
index 279345c..a71a6135 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout.js
@@ -8,8 +8,8 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /**
 The `<iron-flex-layout>` component provides simple ways to use
@@ -40,7 +40,7 @@
     ```
 
     ```js
-    import {html} from '../polymer/lib/utils/html-tag.js';
+    import {html} from '../polymer/polymer_bundled.min.js';
     import '../iron-flex-layout/iron-flex-layout-classes.js';
 
     const template = html`
diff --git a/third_party/polymer/v3_0/components-chromium/iron-icon/iron-icon.js b/third_party/polymer/v3_0/components-chromium/iron-icon/iron-icon.js
index 001d8d58..35835d2b 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-icon/iron-icon.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-icon/iron-icon.js
@@ -11,10 +11,10 @@
 import '../iron-flex-layout/iron-flex-layout.js';
 
 import {IronMeta} from '../iron-meta/iron-meta.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
-import {Base} from '../polymer/polymer-legacy.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
+import {Base} from '../polymer/polymer_bundled.min.js';
 
 /**
 
diff --git a/third_party/polymer/v3_0/components-chromium/iron-iconset-svg/iron-iconset-svg.js b/third_party/polymer/v3_0/components-chromium/iron-iconset-svg/iron-iconset-svg.js
index c76a530..9bd6935 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-iconset-svg/iron-iconset-svg.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-iconset-svg/iron-iconset-svg.js
@@ -8,11 +8,11 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronMeta} from '../iron-meta/iron-meta.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 /**
  * The `iron-iconset-svg` element allows users to define their own icon sets
  * that contain svg icons. The svg icon elements should be children of the
diff --git a/third_party/polymer/v3_0/components-chromium/iron-input/iron-input.js b/third_party/polymer/v3_0/components-chromium/iron-input/iron-input.js
index 3c852791..2754f1ae 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-input/iron-input.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-input/iron-input.js
@@ -8,13 +8,13 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronA11yAnnouncer} from '../iron-a11y-announcer/iron-a11y-announcer.js';
 import {IronValidatableBehavior} from '../iron-validatable-behavior/iron-validatable-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /**
 `<iron-input>` is a wrapper to a native `<input>` element, that adds two-way
diff --git a/third_party/polymer/v3_0/components-chromium/iron-list/iron-list.js b/third_party/polymer/v3_0/components-chromium/iron-list/iron-list.js
index 62f6768..28b9353 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-list/iron-list.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-list/iron-list.js
@@ -8,21 +8,21 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
 
 import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js';
 import {IronScrollTargetBehavior} from '../iron-scroll-target-behavior/iron-scroll-target-behavior.js';
-import {OptionalMutableDataBehavior} from '../polymer/lib/legacy/mutable-data-behavior.js';
-import {Polymer as Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {Templatizer} from '../polymer/lib/legacy/templatizer-behavior.js';
-import {animationFrame, idlePeriod, microTask} from '../polymer/lib/utils/async.js';
-import {Debouncer} from '../polymer/lib/utils/debounce.js';
-import {enqueueDebouncer, flush} from '../polymer/lib/utils/flush.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
-import {matches, translate} from '../polymer/lib/utils/path.js';
-import {TemplateInstanceBase} from '../polymer/lib/utils/templatize.js';
+import {OptionalMutableDataBehavior} from '../polymer/polymer_bundled.min.js';
+import {Polymer as Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {Templatizer} from '../polymer/polymer_bundled.min.js';
+import {animationFrame, idlePeriod, microTask} from '../polymer/polymer_bundled.min.js';
+import {Debouncer} from '../polymer/polymer_bundled.min.js';
+import {enqueueDebouncer, flush} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
+import {matches, translate} from '../polymer/polymer_bundled.min.js';
+import {TemplateInstanceBase} from '../polymer/polymer_bundled.min.js';
 
 var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/);
 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
diff --git a/third_party/polymer/v3_0/components-chromium/iron-location/iron-location.js b/third_party/polymer/v3_0/components-chromium/iron-location/iron-location.js
index 5a402bf..89acb286 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-location/iron-location.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-location/iron-location.js
@@ -8,10 +8,10 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 /**
 
 The `iron-location` element manages binding to and from the current URL.
diff --git a/third_party/polymer/v3_0/components-chromium/iron-location/iron-query-params.js b/third_party/polymer/v3_0/components-chromium/iron-location/iron-query-params.js
index 98e10b78d..c307d78 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-location/iron-query-params.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-location/iron-query-params.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
 /**
  * @demo demo/iron-query-params.html
  */
diff --git a/third_party/polymer/v3_0/components-chromium/iron-media-query/iron-media-query.js b/third_party/polymer/v3_0/components-chromium/iron-media-query/iron-media-query.js
index 48cb839..2125b00 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-media-query/iron-media-query.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-media-query/iron-media-query.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
 
 /**
 `iron-media-query` can be used to data bind to a CSS media query.
diff --git a/third_party/polymer/v3_0/components-chromium/iron-meta/iron-meta.js b/third_party/polymer/v3_0/components-chromium/iron-meta/iron-meta.js
index e32da21..acc3c2c 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-meta/iron-meta.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-meta/iron-meta.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
 
 export class IronMeta {
   /**
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-focusables-helper.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-focusables-helper.js
index 40c10a0..d2edaa0 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-focusables-helper.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-focusables-helper.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 
 var p = Element.prototype;
 var matches = p.matches || p.matchesSelector || p.mozMatchesSelector ||
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-backdrop.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-backdrop.js
index 8bd49271..fca79f0 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-backdrop.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-backdrop.js
@@ -8,11 +8,11 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /*
 `iron-overlay-backdrop` is a backdrop used by `Polymer.IronOverlayBehavior`. It
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior.js
index 0f215b1..6200ea12 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior.js
@@ -8,12 +8,12 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronFitBehavior} from '../iron-fit-behavior/iron-fit-behavior.js';
 import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {useShadow} from '../polymer/lib/utils/settings.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {useShadow} from '../polymer/polymer_bundled.min.js';
 
 import {IronFocusablesHelper} from './iron-focusables-helper.js';
 import {IronOverlayManager} from './iron-overlay-manager.js';
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-manager.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-manager.js
index 99768f4..ac72c52 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-manager.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-manager.js
@@ -8,12 +8,12 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import './iron-overlay-backdrop.js';
 
 import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import * as gestures from '../polymer/lib/utils/gestures.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import * as gestures from '../polymer/polymer_bundled.min.js';
 
 /**
  * @struct
@@ -231,7 +231,7 @@
     this.backdropElement.opened = !!overlay;
     // Property observers are not fired until element is attached
     // in Polymer 2.x, so we ensure element is attached if needed.
-    // https://github.com/Polymer/polymer/issues/4526
+    // https://github.com/Polymer/polymer/polymer_bundled.min.js4526
     this.backdropElement.prepare();
   },
 
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-scroll-manager.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-scroll-manager.js
index 156d1435..247be1df 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-scroll-manager.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-scroll-manager.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 /**
  * Used to calculate the scroll direction during touch events.
  * @type {!Object}
diff --git a/third_party/polymer/v3_0/components-chromium/iron-pages/iron-pages.js b/third_party/polymer/v3_0/components-chromium/iron-pages/iron-pages.js
index 2708d9a6..a99c00d 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-pages/iron-pages.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-pages/iron-pages.js
@@ -8,12 +8,12 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js';
 import {IronSelectableBehavior} from '../iron-selector/iron-selectable.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /**
 `iron-pages` is used to select one of its children to show. One use is to cycle
diff --git a/third_party/polymer/v3_0/components-chromium/iron-range-behavior/iron-range-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-range-behavior/iron-range-behavior.js
index 1802854..ec84de0 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-range-behavior/iron-range-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-range-behavior/iron-range-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 /**
  * `iron-range-behavior` provides the behavior for something with a minimum to
diff --git a/third_party/polymer/v3_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.js
index 8a5317e..0dc27e4 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.js
@@ -8,10 +8,10 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {useShadow} from '../polymer/lib/utils/settings.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {useShadow} from '../polymer/polymer_bundled.min.js';
 
 // Contains all connected resizables that do not have a parent.
 var ORPHANS = new Set();
diff --git a/third_party/polymer/v3_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.js
index a9d889bdf..14118df 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 
 /**
  * `Polymer.IronScrollTargetBehavior` allows an element to respond to scroll
diff --git a/third_party/polymer/v3_0/components-chromium/iron-scroll-threshold/iron-scroll-threshold.js b/third_party/polymer/v3_0/components-chromium/iron-scroll-threshold/iron-scroll-threshold.js
index c592f93..a5b87ed 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-scroll-threshold/iron-scroll-threshold.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-scroll-threshold/iron-scroll-threshold.js
@@ -8,11 +8,11 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronScrollTargetBehavior} from '../iron-scroll-target-behavior/iron-scroll-target-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /**
 `iron-scroll-threshold` is a utility element that listens for `scroll` events
diff --git a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-multi-selectable.js b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-multi-selectable.js
index 9a797323..9acb77a 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-multi-selectable.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-multi-selectable.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronSelectableBehavior} from './iron-selectable.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selectable.js b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selectable.js
index 9a4cd9e6..e18d709fa 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selectable.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selectable.js
@@ -8,10 +8,10 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {dashToCamelCase} from '../polymer/lib/utils/case-map.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {dashToCamelCase} from '../polymer/polymer_bundled.min.js';
 
 import {IronSelection} from './iron-selection.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selection.js b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selection.js
index ebdc9e2b..3788264 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selection.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selection.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 export class IronSelection {
   /**
diff --git a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selector.js b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selector.js
index 1991f583..b12ab298 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selector.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selector.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
 
 import {IronMultiSelectableBehavior} from './iron-multi-selectable.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/iron-test-helpers/mock-interactions.js b/third_party/polymer/v3_0/components-chromium/iron-test-helpers/mock-interactions.js
index 13b9380..86b19e61b 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-test-helpers/mock-interactions.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-test-helpers/mock-interactions.js
@@ -9,7 +9,7 @@
  * rights grant found at http://polymer.github.io/PATENTS.txt
  */
 
-import {Base} from '../polymer/polymer-legacy.js';
+import {Base} from '../polymer/polymer_bundled.min.js';
 
 const HAS_NEW_MOUSE = (() => {
   let has = false;
diff --git a/third_party/polymer/v3_0/components-chromium/iron-test-helpers/test-helpers.js b/third_party/polymer/v3_0/components-chromium/iron-test-helpers/test-helpers.js
index af80627..d490b7f 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-test-helpers/test-helpers.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-test-helpers/test-helpers.js
@@ -9,7 +9,7 @@
  * rights grant found at http://polymer.github.io/PATENTS.txt
  */
 
-import {dom, flush} from '../polymer/lib/legacy/polymer.dom.js';
+import {dom, flush} from '../polymer/polymer_bundled.min.js';
 
 /**
  * Forces distribution of light children, and lifecycle callbacks on the
diff --git a/third_party/polymer/v3_0/components-chromium/iron-validatable-behavior/iron-validatable-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-validatable-behavior/iron-validatable-behavior.js
index c0cf7479..7e3dc51 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-validatable-behavior/iron-validatable-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-validatable-behavior/iron-validatable-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronMeta} from '../iron-meta/iron-meta.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-in-animation.js b/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-in-animation.js
index 257320c..51de8dd6 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-in-animation.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-in-animation.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../../polymer/polymer-legacy.js';
+import '../../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../../polymer/lib/legacy/polymer-fn.js';
+import {Polymer} from '../../polymer/polymer_bundled.min.js';
 import {NeonAnimationBehavior} from '../neon-animation-behavior.js';
 /*
 `<fade-in-animation>` animates the opacity of an element from 0 to 1.
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-out-animation.js b/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-out-animation.js
index 83c586e..de7e04a 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-out-animation.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-out-animation.js
@@ -8,9 +8,9 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../../polymer/polymer-legacy.js';
+import '../../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../../polymer/lib/legacy/polymer-fn.js';
+import {Polymer} from '../../polymer/polymer_bundled.min.js';
 import {NeonAnimationBehavior} from '../neon-animation-behavior.js';
 /*
 `<fade-out-animation>` animates the opacity of an element from 1 to 0.
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable-behavior.js
index 14c4709..e8ab42f 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 /**
  * `NeonAnimatableBehavior` is implemented by elements containing
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable.js
index 215593d..cba6aeb 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable.js
@@ -8,11 +8,11 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 import {NeonAnimatableBehavior} from './neon-animatable-behavior.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animated-pages.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animated-pages.js
index 0ea3e08..d4ee33e 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animated-pages.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animated-pages.js
@@ -8,13 +8,13 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js';
 import {IronSelectableBehavior} from '../iron-selector/iron-selectable.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 import {NeonAnimationRunnerBehavior} from './neon-animation-runner-behavior.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-behavior.js
index 33be9de..5897c90 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 /**
  * Use `NeonAnimationBehavior` to implement an animation.
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-runner-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-runner-behavior.js
index c718e87..6749b5b3 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-runner-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-runner-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {NeonAnimatableBehavior} from './neon-animatable-behavior.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animatable-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animatable-behavior.js
index 9f8b10f..e1036c7 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animatable-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animatable-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {NeonAnimatableBehavior} from './neon-animatable-behavior.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animation-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animation-behavior.js
index 9343824..a92bc67 100644
--- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animation-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animation-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {NeonAnimationBehavior} from './neon-animation-behavior.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js
index 23c9908a..158a366 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronButtonState, IronButtonStateImpl} from '../iron-behaviors/iron-button-state.js';
 import {IronControlState} from '../iron-behaviors/iron-control-state.js';
diff --git a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js
index 73a141bf..8e5a3cc 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronButtonState} from '../iron-behaviors/iron-button-state.js';
 import {IronControlState} from '../iron-behaviors/iron-control-state.js';
diff --git a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js
index 7976f0b2..6aba337 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js
@@ -8,11 +8,11 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import '../paper-ripple/paper-ripple.js';
 
 import {IronButtonStateImpl} from '../iron-behaviors/iron-button-state.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
 
 /**
  * `PaperRippleBehavior` dynamically implements a ripple when the element has
diff --git a/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js b/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js
index eb9535c..42dc7218 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js
@@ -12,8 +12,8 @@
 import '../paper-styles/element-styles/paper-material-styles.js';
 
 import {PaperButtonBehavior, PaperButtonBehaviorImpl} from '../paper-behaviors/paper-button-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {html} from '../polymer/polymer-legacy.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 const template = html`
   <style include="paper-material-styles">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js
index 1f7e4b5a..b2dae3c 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 /**
  * Use `Polymer.PaperInputAddonBehavior` to implement an add-on for
diff --git a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-container.js b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-container.js
index 3e724e7e..2777784f 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-container.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-container.js
@@ -8,15 +8,15 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import '../iron-flex-layout/iron-flex-layout.js';
 import '../paper-styles/default-theme.js';
 import '../paper-styles/typography.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {dashToCamelCase} from '../polymer/lib/utils/case-map.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {dashToCamelCase} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 const template = html`
 <custom-style>
   <style is="custom-style">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-error.js b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-error.js
index afd9adc..97589a32 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-error.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-error.js
@@ -8,12 +8,12 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import '../paper-styles/default-theme.js';
 import '../paper-styles/typography.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 import {PaperInputAddonBehavior} from './paper-input-addon-behavior.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/paper-progress/paper-progress.js b/third_party/polymer/v3_0/components-chromium/paper-progress/paper-progress.js
index fdb26f7..53a221c 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-progress/paper-progress.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-progress/paper-progress.js
@@ -8,13 +8,13 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import '../iron-flex-layout/iron-flex-layout.js';
 import '../paper-styles/color.js';
 
 import {IronRangeBehavior} from '../iron-range-behavior/iron-range-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /**
 Material design: [Progress &
diff --git a/third_party/polymer/v3_0/components-chromium/paper-ripple/paper-ripple.js b/third_party/polymer/v3_0/components-chromium/paper-ripple/paper-ripple.js
index 6969cd7..0288d836 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-ripple/paper-ripple.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-ripple/paper-ripple.js
@@ -8,12 +8,12 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 var Utility = {
   distance: function(x1, y1, x2, y2) {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-behavior.js
index ebc060e..8ab7c52 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-behavior.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-behavior.js
@@ -8,7 +8,7 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
 /** @polymerBehavior */
 export const PaperSpinnerBehavior = {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-lite.js b/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-lite.js
index 740be377..2577e3a 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-lite.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-lite.js
@@ -8,12 +8,12 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import '../paper-styles/color.js';
 import './paper-spinner-styles.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 import {PaperSpinnerBehavior} from './paper-spinner-behavior.js';
 
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/global.js b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/global.js
index 2082c8f..9a4d2e2 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/global.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/global.js
@@ -17,7 +17,7 @@
 
 import '../paper-styles-classes.js';
 
-import {html} from '../../polymer/lib/utils/html-tag.js';
+import {html} from '../../polymer/polymer_bundled.min.js';
 const template = html`<style>
 /* Mixins */
 
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/shadow.js b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/shadow.js
index 44dcd290..a88a6e17 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/shadow.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/shadow.js
@@ -17,7 +17,7 @@
 For a set of styles that can be applied to an element, check
 paper-styles/shadow.js.
 */
-import {html} from '../../polymer/lib/utils/html-tag.js';
+import {html} from '../../polymer/polymer_bundled.min.js';
 const template = html`
 <style>
 .shadow-transition {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/typography.js b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/typography.js
index b35f889..0c397e0 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/typography.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/typography.js
@@ -18,7 +18,7 @@
 paper-styles/typography.html.
 */
 import '../../font-roboto/roboto.js';
-import {html} from '../../polymer/lib/utils/html-tag.js';
+import {html} from '../../polymer/polymer_bundled.min.js';
 const template = html`
 <style>
 
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/color.js b/third_party/polymer/v3_0/components-chromium/paper-styles/color.js
index dac6530..0105d3a5 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/color.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/color.js
@@ -9,9 +9,9 @@
 found at http://polymer.github.io/PATENTS.txt
 */
 
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 const template = html`
 <custom-style>
   <style is="custom-style">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/default-theme.js b/third_party/polymer/v3_0/components-chromium/paper-styles/default-theme.js
index 59533eb..9b33502 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/default-theme.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/default-theme.js
@@ -11,10 +11,10 @@
 /* Taken from
  * https://www.google.com/design/spec/style/color.html#color-ui-color-application
  */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import './color.js';
 
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 const template = html`
 <custom-style>
   <style is="custom-style">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/demo-pages.js b/third_party/polymer/v3_0/components-chromium/paper-styles/demo-pages.js
index 2cfb51b..d90c8a9 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/demo-pages.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/demo-pages.js
@@ -18,13 +18,13 @@
 check iron-demo-helpers/demo-pages-shared-styles.html.
 */
 
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import '../iron-flex-layout/iron-flex-layout.js';
 import './color.js';
 import './typography.js';
 import './shadow.js';
 
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 const template = html`<custom-style>
   <style is="custom-style">
     body {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-item-styles.js b/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-item-styles.js
index 3d9d2ab..042f89bc 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-item-styles.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-item-styles.js
@@ -29,12 +29,12 @@
 @demo demo/index.html
 */
 
-import '../../polymer/polymer-legacy.js';
+import '../../polymer/polymer_bundled.min.js';
 import '../color.js';
 import '../default-theme.js';
 import '../typography.js';
 
-import {html} from '../../polymer/lib/utils/html-tag.js';
+import {html} from '../../polymer/polymer_bundled.min.js';
 const template = html`
 <dom-module id="paper-item-styles">
   <template>
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-material-styles.js b/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-material-styles.js
index d2e3e08..298b1ab 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-material-styles.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-material-styles.js
@@ -29,10 +29,10 @@
 @demo demo/index.html
 */
 
-import '../../polymer/polymer-legacy.js';
+import '../../polymer/polymer_bundled.min.js';
 import '../shadow.js';
 
-import {html} from '../../polymer/lib/utils/html-tag.js';
+import {html} from '../../polymer/polymer_bundled.min.js';
 const template = html`
 <dom-module id="paper-material-styles">
   <template>
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/shadow.js b/third_party/polymer/v3_0/components-chromium/paper-styles/shadow.js
index 1e7eb944..36f66c54 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/shadow.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/shadow.js
@@ -9,9 +9,9 @@
 found at http://polymer.github.io/PATENTS.txt
 */
 
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 const template = html`
 <custom-style>
   <style is="custom-style">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/typography.js b/third_party/polymer/v3_0/components-chromium/paper-styles/typography.js
index ec805f0..ed930d05 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/typography.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/typography.js
@@ -17,10 +17,10 @@
 Design typography section.
 */
 
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 import '../font-roboto/roboto.js';
 
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 const template = html`<custom-style>
   <style is="custom-style">
     html {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js b/third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js
index 1599a28..aaa6e15 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js
@@ -8,11 +8,11 @@
 part of the polymer project is also subject to an additional IP rights grant
 found at http://polymer.github.io/PATENTS.txt
 */
-import '../polymer/polymer-legacy.js';
+import '../polymer/polymer_bundled.min.js';
 
-import {Polymer} from '../polymer/lib/legacy/polymer-fn.js';
-import {dom} from '../polymer/lib/legacy/polymer.dom.js';
-import {html} from '../polymer/lib/utils/html-tag.js';
+import {Polymer} from '../polymer/polymer_bundled.min.js';
+import {dom} from '../polymer/polymer_bundled.min.js';
+import {html} from '../polymer/polymer_bundled.min.js';
 
 /**
 Material design:
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/closure-types.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/closure-types.js
deleted file mode 100644
index 86ddb08..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/externs/closure-types.js
+++ /dev/null
@@ -1,1588 +0,0 @@
-/**
- * @fileoverview Generated typings for Polymer mixins
- * @externs
- * @suppress {checkPrototypalTypes}
- *
- * @license
- * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
- * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
- * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
- * Code distributed by Google as part of the polymer project is also
- * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
- */
-/* eslint-disable */
-/**
-* @interface
-*/
-function Polymer_PropertiesChanged(){}
-/** @type {undefined} */
-Polymer_PropertiesChanged.prototype.__dataEnabled;
-
-/**
-* @override
-* @param {string} property Name of the property
-* @param {boolean=} readOnly When true, no setter is created; the
-  protected `_setProperty` function must be used to set the property
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._createPropertyAccessor = function(property, readOnly){};
-/**
-* @override
-* @param {string} property Name of the property
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._addPropertyToAttributeMap = function(property){};
-/**
-* @override
-* @param {string} property Name of the property
-* @param {boolean=} readOnly When true, no setter is created
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._definePropertyAccessor = function(property, readOnly){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype.ready = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._initializeProperties = function(){};
-/**
-* @override
-* @param {Object} props Bag of property values that were overwritten
-  when creating property accessors.
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._initializeInstanceProperties = function(props){};
-/**
-* @override
-* @param {string} property Name of the property
-* @param {*} value Value to set
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._setProperty = function(property, value){};
-/**
-* @override
-* @param {string} property Name of property
-* @return {*}
-*/
-Polymer_PropertiesChanged.prototype._getProperty = function(property){};
-/**
-* @override
-* @param {string} property Name of the property
-* @param {*} value Value to set
-* @param {boolean=} ext Not used here; affordance for closure
-* @return {boolean}
-*/
-Polymer_PropertiesChanged.prototype._setPendingProperty = function(property, value, ext){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._invalidateProperties = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._enableProperties = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._flushProperties = function(){};
-/**
-* @override
-* @param {!Object} currentProps Bag of all current accessor values
-* @param {?Object} changedProps Bag of properties changed since the last
-  call to `_propertiesChanged`
-* @param {?Object} oldProps Bag of previous values for each property
-  in `changedProps`
-* @return {boolean}
-*/
-Polymer_PropertiesChanged.prototype._shouldPropertiesChange = function(currentProps, changedProps, oldProps){};
-/**
-* @override
-* @param {!Object} currentProps Bag of all current accessor values
-* @param {?Object} changedProps Bag of properties changed since the last
-  call to `_propertiesChanged`
-* @param {?Object} oldProps Bag of previous values for each property
-  in `changedProps`
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._propertiesChanged = function(currentProps, changedProps, oldProps){};
-/**
-* @override
-* @param {string} property Property name
-* @param {*} value New property value
-* @param {*} old Previous property value
-* @return {boolean}
-*/
-Polymer_PropertiesChanged.prototype._shouldPropertyChange = function(property, value, old){};
-/**
-* @override
-* @param {string} name Name of attribute that changed
-* @param {?string} old Old attribute value
-* @param {?string} value New attribute value
-* @param {?string=} namespace Attribute namespace.
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype.attributeChangedCallback = function(name, old, value, namespace){};
-/**
-* @override
-* @param {string} attribute Name of attribute to deserialize.
-* @param {?string} value of the attribute.
-* @param {*=} type type to deserialize to, defaults to the value
-returned from `typeForProperty`
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._attributeToProperty = function(attribute, value, type){};
-/**
-* @override
-* @param {string} property Property name to reflect.
-* @param {string=} attribute Attribute name to reflect to.
-* @param {*=} value Property value to refect.
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._propertyToAttribute = function(property, attribute, value){};
-/**
-* @override
-* @param {Element} node Element to set attribute to.
-* @param {*} value Value to serialize.
-* @param {string} attribute Attribute name to serialize to.
-* @return {void}
-*/
-Polymer_PropertiesChanged.prototype._valueToNodeAttribute = function(node, value, attribute){};
-/**
-* @override
-* @param {*} value Property value to serialize.
-* @return {(string | undefined)}
-*/
-Polymer_PropertiesChanged.prototype._serializeValue = function(value){};
-/**
-* @override
-* @param {?string} value Value to deserialize.
-* @param {*=} type Type to deserialize the string to.
-* @return {*}
-*/
-Polymer_PropertiesChanged.prototype._deserializeValue = function(value, type){};
-/**
-* @param {!Object} props Object whose keys are names of accessors.
-* @return {void}
-*/
-Polymer_PropertiesChanged.createProperties = function(props){};
-/**
-* @param {string} property Property to convert
-* @return {string}
-*/
-Polymer_PropertiesChanged.attributeNameForProperty = function(property){};
-/**
-* @param {string} name Name of property
-* @return {void}
-*/
-Polymer_PropertiesChanged.typeForProperty = function(name){};
-/**
-* @interface
-* @extends {Polymer_PropertiesChanged}
-*/
-function Polymer_PropertyAccessors(){}
-/**
-* @override
-* @param {string} property Name of the property
-* @param {boolean=} readOnly When true, no setter is created
-
-When calling on a prototype, any overwritten values are saved in
-`__dataProto`, and it is up to the subclasser to decide how/when
-to set those properties back into the accessor.  When calling on an
-instance, the overwritten value is set via `_setPendingProperty`,
-and the user should call `_invalidateProperties` or `_flushProperties`
-for the values to take effect.
-* @return {void}
-*/
-Polymer_PropertyAccessors.prototype._definePropertyAccessor = function(property, readOnly){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertyAccessors.prototype._initializeProperties = function(){};
-/**
-* @override
-* @param {*} value Property value to serialize.
-* @return {(string | undefined)}
-*/
-Polymer_PropertyAccessors.prototype._serializeValue = function(value){};
-/**
-* @override
-* @param {?string} value Attribute value to deserialize.
-* @param {*=} type Type to deserialize the string to.
-* @return {*}
-*/
-Polymer_PropertyAccessors.prototype._deserializeValue = function(value, type){};
-/**
-* @override
-* @param {Object} props Bag of property values that were overwritten
-  when creating property accessors.
-* @return {void}
-*/
-Polymer_PropertyAccessors.prototype._initializeProtoProperties = function(props){};
-/**
-* @override
-* @param {string} attribute Name of attribute to ensure is set.
-* @param {string} value of the attribute.
-* @return {void}
-*/
-Polymer_PropertyAccessors.prototype._ensureAttribute = function(attribute, value){};
-/**
-* @override
-* @param {string} property Property name
-* @return {boolean}
-*/
-Polymer_PropertyAccessors.prototype._hasAccessor = function(property){};
-/**
-* @override
-* @param {string} prop Property name
-* @return {boolean}
-*/
-Polymer_PropertyAccessors.prototype._isPropertyPending = function(prop){};
-/**
-* @param {string} property Property to convert
-* @return {string}
-*/
-Polymer_PropertyAccessors.attributeNameForProperty = function(property){};
-/**
-* @return {void}
-*/
-Polymer_PropertyAccessors.createPropertiesForAttributes = function(){};
-/**
-* @interface
-*/
-function Polymer_TemplateStamp(){}
-/**
-* @override
-* @param {!HTMLTemplateElement} template Template to stamp
-* @return {!StampedTemplate}
-*/
-Polymer_TemplateStamp.prototype._stampTemplate = function(template){};
-/**
-* @override
-* @param {!EventTarget} node Node to add listener on
-* @param {string} eventName Name of event
-* @param {string} methodName Name of method
-* @param {*=} context Context the method will be called on (defaults
-  to `node`)
-* @return {Function}
-*/
-Polymer_TemplateStamp.prototype._addMethodEventListenerToNode = function(node, eventName, methodName, context){};
-/**
-* @override
-* @param {!EventTarget} node Node to add event listener to
-* @param {string} eventName Name of event
-* @param {function (!Event): void} handler Listener function to add
-* @return {void}
-*/
-Polymer_TemplateStamp.prototype._addEventListenerToNode = function(node, eventName, handler){};
-/**
-* @override
-* @param {!EventTarget} node Node to remove event listener from
-* @param {string} eventName Name of event
-* @param {function (!Event): void} handler Listener function to remove
-* @return {void}
-*/
-Polymer_TemplateStamp.prototype._removeEventListenerFromNode = function(node, eventName, handler){};
-/**
-* @param {!HTMLTemplateElement} template Template to parse
-* @param {TemplateInfo=} outerTemplateInfo Template metadata from the outer
-  template, for parsing nested templates
-* @return {!TemplateInfo}
-*/
-Polymer_TemplateStamp._parseTemplate = function(template, outerTemplateInfo){};
-/**
-* @param {*} template 
-* @param {*} templateInfo 
-* @param {*} nodeInfo 
-*/
-Polymer_TemplateStamp._parseTemplateContent = function(template, templateInfo, nodeInfo){};
-/**
-* @param {Node} node Node to parse
-* @param {!TemplateInfo} templateInfo Template metadata for current template
-* @param {!NodeInfo} nodeInfo Node metadata for current template.
-* @return {boolean}
-*/
-Polymer_TemplateStamp._parseTemplateNode = function(node, templateInfo, nodeInfo){};
-/**
-* @param {Node} root Root node whose `childNodes` will be parsed
-* @param {!TemplateInfo} templateInfo Template metadata for current template
-* @param {!NodeInfo} nodeInfo Node metadata for current template.
-* @return {void}
-*/
-Polymer_TemplateStamp._parseTemplateChildNodes = function(root, templateInfo, nodeInfo){};
-/**
-* @param {HTMLTemplateElement} node Node to parse (a <template>)
-* @param {TemplateInfo} outerTemplateInfo Template metadata for current template
-  that includes the template `node`
-* @param {!NodeInfo} nodeInfo Node metadata for current template.
-* @return {boolean}
-*/
-Polymer_TemplateStamp._parseTemplateNestedTemplate = function(node, outerTemplateInfo, nodeInfo){};
-/**
-* @param {Element} node Node to parse
-* @param {TemplateInfo} templateInfo Template metadata for current template
-* @param {NodeInfo} nodeInfo Node metadata for current template.
-* @return {boolean}
-*/
-Polymer_TemplateStamp._parseTemplateNodeAttributes = function(node, templateInfo, nodeInfo){};
-/**
-* @param {Element} node Node to parse
-* @param {!TemplateInfo} templateInfo Template metadata for current template
-* @param {!NodeInfo} nodeInfo Node metadata for current template.
-* @param {string} name Attribute name
-* @param {string} value Attribute value
-* @return {boolean}
-*/
-Polymer_TemplateStamp._parseTemplateNodeAttribute = function(node, templateInfo, nodeInfo, name, value){};
-/**
-* @param {HTMLTemplateElement} template Template to retrieve `content` for
-* @return {DocumentFragment}
-*/
-Polymer_TemplateStamp._contentForTemplate = function(template){};
-/**
-* @interface
-* @extends {Polymer_TemplateStamp}
-* @extends {Polymer_PropertyAccessors}
-*/
-function Polymer_PropertyEffects(){}
-/** @type {boolean} */
-Polymer_PropertyEffects.prototype.__dataClientsReady;
-
-/** @type {Array} */
-Polymer_PropertyEffects.prototype.__dataPendingClients;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__dataToNotify;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__dataLinkedPaths;
-
-/** @type {boolean} */
-Polymer_PropertyEffects.prototype.__dataHasPaths;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__dataCompoundStorage;
-
-/** @type {Polymer_PropertyEffects} */
-Polymer_PropertyEffects.prototype.__dataHost;
-
-/** @type {!Object} */
-Polymer_PropertyEffects.prototype.__dataTemp;
-
-/** @type {boolean} */
-Polymer_PropertyEffects.prototype.__dataClientsInitialized;
-
-/** @type {!Object} */
-Polymer_PropertyEffects.prototype.__data;
-
-/** @type {(!Object | null)} */
-Polymer_PropertyEffects.prototype.__dataPending;
-
-/** @type {!Object} */
-Polymer_PropertyEffects.prototype.__dataOld;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__computeEffects;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__reflectEffects;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__notifyEffects;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__propagateEffects;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__observeEffects;
-
-/** @type {Object} */
-Polymer_PropertyEffects.prototype.__readOnly;
-
-/** @type {!TemplateInfo} */
-Polymer_PropertyEffects.prototype.__templateInfo;
-
-/** @type {undefined} */
-Polymer_PropertyEffects.prototype.PROPERTY_EFFECT_TYPES;
-
-/**
-* @override
-* @param {!HTMLTemplateElement} template Template to stamp
-* @return {!StampedTemplate}
-*/
-Polymer_PropertyEffects.prototype._stampTemplate = function(template){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype.ready = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._initializeProperties = function(){};
-/**
-* @override
-* @param {Object} props Properties to initialize on the instance
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._initializeInstanceProperties = function(props){};
-/**
-* @override
-* @param {string} property Name of the property
-* @param {*} value Value to set
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._setProperty = function(property, value){};
-/**
-* @override
-* @param {string} property Name of the property
-* @param {*} value Value to set
-* @param {boolean=} shouldNotify True if property should fire notification
-  event (applies only for `notify: true` properties)
-* @return {boolean}
-*/
-Polymer_PropertyEffects.prototype._setPendingProperty = function(property, value, shouldNotify){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._invalidateProperties = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._flushProperties = function(){};
-/**
-* @override
-* @param {!Object} currentProps Bag of all current accessor values
-* @param {?Object} changedProps Bag of properties changed since the last
-  call to `_propertiesChanged`
-* @param {?Object} oldProps Bag of previous values for each property
-  in `changedProps`
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._propertiesChanged = function(currentProps, changedProps, oldProps){};
-/**
-* @override
-* @param {Object} props Properties to initialize on the prototype
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._initializeProtoProperties = function(props){};
-/**
-* @override
-* @param {string} property Property that should trigger the effect
-* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
-* @param {Object=} effect Effect metadata object
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._addPropertyEffect = function(property, type, effect){};
-/**
-* @override
-* @param {string} property Property the effect was associated with
-* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
-* @param {Object=} effect Effect metadata object to remove
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._removePropertyEffect = function(property, type, effect){};
-/**
-* @override
-* @param {string} property Property name
-* @param {string=} type Effect type, from this.PROPERTY_EFFECT_TYPES
-* @return {boolean}
-*/
-Polymer_PropertyEffects.prototype._hasPropertyEffect = function(property, type){};
-/**
-* @override
-* @param {string} property Property name
-* @return {boolean}
-*/
-Polymer_PropertyEffects.prototype._hasReadOnlyEffect = function(property){};
-/**
-* @override
-* @param {string} property Property name
-* @return {boolean}
-*/
-Polymer_PropertyEffects.prototype._hasNotifyEffect = function(property){};
-/**
-* @override
-* @param {string} property Property name
-* @return {boolean}
-*/
-Polymer_PropertyEffects.prototype._hasReflectEffect = function(property){};
-/**
-* @override
-* @param {string} property Property name
-* @return {boolean}
-*/
-Polymer_PropertyEffects.prototype._hasComputedEffect = function(property){};
-/**
-* @override
-* @param {(string | !Array.<(number | string)>)} path Path to set
-* @param {*} value Value to set
-* @param {boolean=} shouldNotify Set to true if this change should
- cause a property notification event dispatch
-* @param {boolean=} isPathNotification If the path being set is a path
-  notification of an already changed value, as opposed to a request
-  to set and notify the change.  In the latter `false` case, a dirty
-  check is performed and then the value is set to the path before
-  enqueuing the pending property change.
-* @return {boolean}
-*/
-Polymer_PropertyEffects.prototype._setPendingPropertyOrPath = function(path, value, shouldNotify, isPathNotification){};
-/**
-* @override
-* @param {!Node} node The node to set a property on
-* @param {string} prop The property to set
-* @param {*} value The value to set
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._setUnmanagedPropertyToNode = function(node, prop, value){};
-/**
-* @override
-* @param {Object} client PropertyEffects client to enqueue
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._enqueueClient = function(client){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._flushClients = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._readyClients = function(){};
-/**
-* @override
-* @param {Object} props Bag of one or more key-value pairs whose key is
-  a property and value is the new value to set for that property.
-* @param {boolean=} setReadOnly When true, any private values set in
-  `props` will be set. By default, `setProperties` will not set
-  `readOnly: true` root properties.
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype.setProperties = function(props, setReadOnly){};
-/**
-* @override
-* @param {Object} changedProps Bag of changed properties
-* @param {Object} oldProps Bag of previous values for changed properties
-* @param {boolean} hasPaths True with `props` contains one or more paths
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._propagatePropertyChanges = function(changedProps, oldProps, hasPaths){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} to Target path to link.
-* @param {(string | !Array.<(string | number)>)} from Source path to link.
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype.linkPaths = function(to, from){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} path Target path to unlink.
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype.unlinkPaths = function(path){};
-/**
-* @override
-* @param {string} path Path that should be notified.
-* @param {Array} splices Array of splice records indicating ordered
-  changes that occurred to the array. Each record should have the
-  following fields:
-   * index: index at which the change occurred
-   * removed: array of items that were removed from this index
-   * addedCount: number of new items added at this index
-   * object: a reference to the array in question
-   * type: the string literal 'splice'
-
-  Note that splice records _must_ be normalized such that they are
-  reported in index order (raw results from `Object.observe` are not
-  ordered and must be normalized/merged before notifying).
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype.notifySplices = function(path, splices){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} path Path to the value
-  to read.  The path may be specified as a string (e.g. `foo.bar.baz`)
-  or an array of path parts (e.g. `['foo.bar', 'baz']`).  Note that
-  bracketed expressions are not supported; string-based path parts
-  *must* be separated by dots.  Note that when dereferencing array
-  indices, the index may be used as a dotted part directly
-  (e.g. `users.12.name` or `['users', 12, 'name']`).
-* @param {Object=} root Root object from which the path is evaluated.
-* @return {*}
-*/
-Polymer_PropertyEffects.prototype.get = function(path, root){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} path Path to the value
-  to write.  The path may be specified as a string (e.g. `'foo.bar.baz'`)
-  or an array of path parts (e.g. `['foo.bar', 'baz']`).  Note that
-  bracketed expressions are not supported; string-based path parts
-  *must* be separated by dots.  Note that when dereferencing array
-  indices, the index may be used as a dotted part directly
-  (e.g. `'users.12.name'` or `['users', 12, 'name']`).
-* @param {*} value Value to set at the specified path.
-* @param {Object=} root Root object from which the path is evaluated.
-  When specified, no notification will occur.
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype.set = function(path, value, root){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} path Path to array.
-* @param {...*} items Items to push onto array
-* @return {number}
-*/
-Polymer_PropertyEffects.prototype.push = function(path, items){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} path Path to array.
-* @return {*}
-*/
-Polymer_PropertyEffects.prototype.pop = function(path){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} path Path to array.
-* @param {number} start Index from which to start removing/inserting.
-* @param {number=} deleteCount Number of items to remove.
-* @param {...*} items Items to insert into array.
-* @return {Array}
-*/
-Polymer_PropertyEffects.prototype.splice = function(path, start, deleteCount, items){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} path Path to array.
-* @return {*}
-*/
-Polymer_PropertyEffects.prototype.shift = function(path){};
-/**
-* @override
-* @param {(string | !Array.<(string | number)>)} path Path to array.
-* @param {...*} items Items to insert info array
-* @return {number}
-*/
-Polymer_PropertyEffects.prototype.unshift = function(path, items){};
-/**
-* @override
-* @param {string} path Path that should be notified.
-* @param {*=} value Value at the path (optional).
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype.notifyPath = function(path, value){};
-/**
-* @override
-* @param {string} property Property name
-* @param {boolean=} protectedSetter Creates a custom protected setter
-  when `true`.
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._createReadOnlyProperty = function(property, protectedSetter){};
-/**
-* @override
-* @param {string} property Property name
-* @param {(string | function (*, *))} method Function or name of observer method
-    to call
-* @param {boolean=} dynamicFn Whether the method name should be included as
-  a dependency to the effect.
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._createPropertyObserver = function(property, method, dynamicFn){};
-/**
-* @override
-* @param {string} expression Method expression
-* @param {(boolean | Object)=} dynamicFn Boolean or object map indicating
-  whether method names should be included as a dependency to the effect.
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._createMethodObserver = function(expression, dynamicFn){};
-/**
-* @override
-* @param {string} property Property name
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._createNotifyingProperty = function(property){};
-/**
-* @override
-* @param {string} property Property name
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._createReflectedProperty = function(property){};
-/**
-* @override
-* @param {string} property Name of computed property to set
-* @param {string} expression Method expression
-* @param {(boolean | Object)=} dynamicFn Boolean or object map indicating
-  whether method names should be included as a dependency to the effect.
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._createComputedProperty = function(property, expression, dynamicFn){};
-/**
-* @override
-* @param {!HTMLTemplateElement} template Template containing binding
-  bindings
-* @param {boolean=} instanceBinding When false (default), performs
-  "prototypical" binding of the template and overwrites any previously
-  bound template for the class. When true (as passed from
-  `_stampTemplate`), the template info is instanced and linked into
-  the list of bound templates.
-* @return {!TemplateInfo}
-*/
-Polymer_PropertyEffects.prototype._bindTemplate = function(template, instanceBinding){};
-/**
-* @override
-* @param {!StampedTemplate} dom DocumentFragment previously returned
-  from `_stampTemplate` associated with the nodes to be removed
-* @return {void}
-*/
-Polymer_PropertyEffects.prototype._removeBoundDom = function(dom){};
-/**
-* @param {Node} node Node to parse
-* @param {TemplateInfo} templateInfo Template metadata for current template
-* @param {NodeInfo} nodeInfo Node metadata for current template node
-* @return {boolean}
-*/
-Polymer_PropertyEffects._parseTemplateNode = function(node, templateInfo, nodeInfo){};
-/**
-* @param {Node} node Node to parse
-* @param {TemplateInfo} templateInfo Template metadata for current template
-* @param {NodeInfo} nodeInfo Node metadata for current template node
-* @return {boolean}
-*/
-Polymer_PropertyEffects._parseTemplateNestedTemplate = function(node, templateInfo, nodeInfo){};
-/**
-* @param {Element} node Node to parse
-* @param {TemplateInfo} templateInfo Template metadata for current template
-* @param {NodeInfo} nodeInfo Node metadata for current template node
-* @param {string} name Attribute name
-* @param {string} value Attribute value
-* @return {boolean}
-*/
-Polymer_PropertyEffects._parseTemplateNodeAttribute = function(node, templateInfo, nodeInfo, name, value){};
-/**
-* @param {string} property Property that should trigger the effect
-* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
-* @param {Object=} effect Effect metadata object
-* @return {void}
-*/
-Polymer_PropertyEffects.addPropertyEffect = function(property, type, effect){};
-/**
-* @param {string} property Property name
-* @param {(string | function (*, *))} method Function or name of observer method to call
-* @param {boolean=} dynamicFn Whether the method name should be included as
-  a dependency to the effect.
-* @return {void}
-*/
-Polymer_PropertyEffects.createPropertyObserver = function(property, method, dynamicFn){};
-/**
-* @param {string} expression Method expression
-* @param {(boolean | Object)=} dynamicFn Boolean or object map indicating
-* @return {void}
-*/
-Polymer_PropertyEffects.createMethodObserver = function(expression, dynamicFn){};
-/**
-* @param {string} property Property name
-* @return {void}
-*/
-Polymer_PropertyEffects.createNotifyingProperty = function(property){};
-/**
-* @param {string} property Property name
-* @param {boolean=} protectedSetter Creates a custom protected setter
-  when `true`.
-* @return {void}
-*/
-Polymer_PropertyEffects.createReadOnlyProperty = function(property, protectedSetter){};
-/**
-* @param {string} property Property name
-* @return {void}
-*/
-Polymer_PropertyEffects.createReflectedProperty = function(property){};
-/**
-* @param {string} property Name of computed property to set
-* @param {string} expression Method expression
-* @param {(boolean | Object)=} dynamicFn Boolean or object map indicating whether
-  method names should be included as a dependency to the effect.
-* @return {void}
-*/
-Polymer_PropertyEffects.createComputedProperty = function(property, expression, dynamicFn){};
-/**
-* @param {!HTMLTemplateElement} template Template containing binding
-  bindings
-* @return {!TemplateInfo}
-*/
-Polymer_PropertyEffects.bindTemplate = function(template){};
-/**
-* @param {Object} templateInfo Template metadata to add effect to
-* @param {string} prop Property that should trigger the effect
-* @param {Object=} effect Effect metadata object
-* @return {void}
-*/
-Polymer_PropertyEffects._addTemplatePropertyEffect = function(templateInfo, prop, effect){};
-/**
-* @param {string} text Text to parse from attribute or textContent
-* @param {Object} templateInfo Current template metadata
-* @return {Array.<!BindingPart>}
-*/
-Polymer_PropertyEffects._parseBindings = function(text, templateInfo){};
-/**
-* @param {!Polymer_PropertyEffects} inst Element that should be used as
-    scope for binding dependencies
-* @param {BindingPart} part Binding part metadata
-* @param {string} path Property/path that triggered this effect
-* @param {Object} props Bag of current property changes
-* @param {Object} oldProps Bag of previous values for changed properties
-* @param {boolean} hasPaths True with `props` contains one or more paths
-* @return {*}
-*/
-Polymer_PropertyEffects._evaluateBinding = function(inst, part, path, props, oldProps, hasPaths){};
-/**
-* @interface
-* @extends {Polymer_PropertiesChanged}
-*/
-function Polymer_PropertiesMixin(){}
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertiesMixin.prototype._initializeProperties = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertiesMixin.prototype.connectedCallback = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_PropertiesMixin.prototype.disconnectedCallback = function(){};
-/**
-* @param {string} name Name of property
-* @return {*}
-*/
-Polymer_PropertiesMixin.typeForProperty = function(name){};
-/**
-* @return {void}
-*/
-Polymer_PropertiesMixin.finalize = function(){};
-/**
-* @return {void}
-*/
-Polymer_PropertiesMixin._finalizeClass = function(){};
-/**
-* @interface
-* @extends {Polymer_PropertyEffects}
-* @extends {Polymer_PropertiesMixin}
-*/
-function Polymer_ElementMixin(){}
-/** @type {HTMLTemplateElement} */
-Polymer_ElementMixin.prototype._template;
-
-/** @type {string} */
-Polymer_ElementMixin.prototype._importPath;
-
-/** @type {string} */
-Polymer_ElementMixin.prototype.rootPath;
-
-/** @type {string} */
-Polymer_ElementMixin.prototype.importPath;
-
-/** @type {(StampedTemplate | HTMLElement | ShadowRoot)} */
-Polymer_ElementMixin.prototype.root;
-
-/** @type {!Object.<string, !Element>} */
-Polymer_ElementMixin.prototype.$;
-
-/**
-* @override
-* @return {void}
-*/
-Polymer_ElementMixin.prototype.ready = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_ElementMixin.prototype._initializeProperties = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_ElementMixin.prototype._readyClients = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_ElementMixin.prototype.connectedCallback = function(){};
-/**
-* @override
-* @param {StampedTemplate} dom to attach to the element.
-* @return {ShadowRoot}
-*/
-Polymer_ElementMixin.prototype._attachDom = function(dom){};
-/**
-* @override
-* @param {Object=} properties Bag of custom property key/values to
-  apply to this element.
-* @return {void}
-*/
-Polymer_ElementMixin.prototype.updateStyles = function(properties){};
-/**
-* @override
-* @param {string} url URL to resolve.
-* @param {string=} base Optional base URL to resolve against, defaults
-to the element's `importPath`
-* @return {string}
-*/
-Polymer_ElementMixin.prototype.resolveUrl = function(url, base){};
-/**
-* @param {!HTMLTemplateElement} template Template
-* @param {!TemplateInfo} templateInfo Template metadata for current template
-* @param {!NodeInfo} nodeInfo Node metadata for current template.
-* @return {boolean}
-*/
-Polymer_ElementMixin._parseTemplateContent = function(template, templateInfo, nodeInfo){};
-/**
-* @param {!Object} props .
-* @return {void}
-*/
-Polymer_ElementMixin.createProperties = function(props){};
-/**
-* @param {Object} templateInfo Template metadata to add effect to
-* @param {string} prop Property that should trigger the effect
-* @param {Object=} effect Effect metadata object
-* @return {void}
-*/
-Polymer_ElementMixin._addTemplatePropertyEffect = function(templateInfo, prop, effect){};
-/**
-* @return {void}
-*/
-Polymer_ElementMixin._finalizeClass = function(){};
-/**
-* @return {void}
-*/
-Polymer_ElementMixin._prepareTemplate = function(){};
-/**
-* @param {Object} observers Array of observer descriptors for
-  this class
-* @param {Object} dynamicFns Object containing keys for any properties
-  that are functions and should trigger the effect when the function
-  reference is changed
-* @return {void}
-*/
-Polymer_ElementMixin.createObservers = function(observers, dynamicFns){};
-/**
-* @param {string} cssText Text containing styling to process
-* @param {string} baseURI Base URI to rebase CSS paths against
-* @return {string}
-*/
-Polymer_ElementMixin._processStyleText = function(cssText, baseURI){};
-/**
-* @param {string} is Tag name (or type extension name) for this element
-* @return {void}
-*/
-Polymer_ElementMixin._finalizeTemplate = function(is){};
-/**
-* @interface
-*/
-function Polymer_GestureEventListeners(){}
-/**
-* @override
-* @param {!EventTarget} node Node to add event listener to
-* @param {string} eventName Name of event
-* @param {function (!Event): void} handler Listener function to add
-* @return {void}
-*/
-Polymer_GestureEventListeners.prototype._addEventListenerToNode = function(node, eventName, handler){};
-/**
-* @override
-* @param {!EventTarget} node Node to remove event listener from
-* @param {string} eventName Name of event
-* @param {function (!Event): void} handler Listener function to remove
-* @return {void}
-*/
-Polymer_GestureEventListeners.prototype._removeEventListenerFromNode = function(node, eventName, handler){};
-/**
-* @interface
-* @extends {Polymer_PropertyAccessors}
-*/
-function Polymer_DirMixin(){}
-/** @type {boolean} */
-Polymer_DirMixin.prototype.__autoDirOptOut;
-
-/**
-* @override
-* @return {void}
-*/
-Polymer_DirMixin.prototype.ready = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_DirMixin.prototype.connectedCallback = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_DirMixin.prototype.disconnectedCallback = function(){};
-/**
-* @param {string} cssText .
-* @param {string} baseURI .
-* @return {string}
-*/
-Polymer_DirMixin._processStyleText = function(cssText, baseURI){};
-/**
-* @param {string} text CSS text to replace DIR
-* @return {string}
-*/
-Polymer_DirMixin._replaceDirInCssText = function(text){};
-/**
-* @interface
-* @extends {Polymer_ElementMixin}
-* @extends {Polymer_GestureEventListeners}
-*/
-function Polymer_LegacyElementMixin(){}
-/** @type {boolean} */
-Polymer_LegacyElementMixin.prototype.isAttached;
-
-/** @type {?WeakMap.<!Element, !Object.<string, !Function>>} */
-Polymer_LegacyElementMixin.prototype.__boundListeners;
-
-/** @type {?Object.<string, ?Function>} */
-Polymer_LegacyElementMixin.prototype._debouncers;
-
-/** @type {undefined} */
-Polymer_LegacyElementMixin.prototype.domHost;
-
-/** @type {string} */
-Polymer_LegacyElementMixin.prototype.is;
-
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.ready = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype._initializeProperties = function(){};
-/**
-* @override
-* @param {string} name Name of attribute.
-* @param {?string} old Old value of attribute.
-* @param {?string} value Current value of attribute.
-* @param {?string} namespace Attribute namespace.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.attributeChangedCallback = function(name, old, value, namespace){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.connectedCallback = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.disconnectedCallback = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.created = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.attached = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.detached = function(){};
-/**
-* @override
-* @param {string} name Name of attribute.
-* @param {?string} old Old value of attribute.
-* @param {?string} value Current value of attribute.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.attributeChanged = function(name, old, value){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype._registered = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype._ensureAttributes = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype._applyListeners = function(){};
-/**
-* @override
-* @param {*} value Value to deserialize
-* @return {(string | undefined)}
-*/
-Polymer_LegacyElementMixin.prototype.serialize = function(value){};
-/**
-* @override
-* @param {string} value String to deserialize
-* @param {*} type Type to deserialize the string to
-* @return {*}
-*/
-Polymer_LegacyElementMixin.prototype.deserialize = function(value, type){};
-/**
-* @override
-* @param {string} property Property name to reflect.
-* @param {string=} attribute Attribute name to reflect.
-* @param {*=} value Property value to reflect.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.reflectPropertyToAttribute = function(property, attribute, value){};
-/**
-* @override
-* @param {*} value Value to serialize.
-* @param {string} attribute Attribute name to serialize to.
-* @param {Element} node Element to set attribute to.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.serializeValueToAttribute = function(value, attribute, node){};
-/**
-* @override
-* @param {Object} prototype Target object to copy properties to.
-* @param {Object} api Source object to copy properties from.
-* @return {Object}
-*/
-Polymer_LegacyElementMixin.prototype.extend = function(prototype, api){};
-/**
-* @override
-* @param {!Object} target Target object to copy properties to.
-* @param {!Object} source Source object to copy properties from.
-* @return {!Object}
-*/
-Polymer_LegacyElementMixin.prototype.mixin = function(target, source){};
-/**
-* @override
-* @param {Object} object The object on which to set the prototype.
-* @param {Object} prototype The prototype that will be set on the given
-`object`.
-* @return {Object}
-*/
-Polymer_LegacyElementMixin.prototype.chainObject = function(object, prototype){};
-/**
-* @override
-* @param {HTMLTemplateElement} template HTML template element to instance.
-* @return {!DocumentFragment}
-*/
-Polymer_LegacyElementMixin.prototype.instanceTemplate = function(template){};
-/**
-* @override
-* @param {string} type Name of event type.
-* @param {*=} detail Detail value containing event-specific
-  payload.
-* @param {{bubbles: (boolean | undefined), cancelable: (boolean | undefined), composed: (boolean | undefined)}=} options Object specifying options.  These may include:
- `bubbles` (boolean, defaults to `true`),
- `cancelable` (boolean, defaults to false), and
- `node` on which to fire the event (HTMLElement, defaults to `this`).
-* @return {!Event}
-*/
-Polymer_LegacyElementMixin.prototype.fire = function(type, detail, options){};
-/**
-* @override
-* @param {?EventTarget} node Element to add event listener to.
-* @param {string} eventName Name of event to listen for.
-* @param {string} methodName Name of handler method on `this` to call.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.listen = function(node, eventName, methodName){};
-/**
-* @override
-* @param {?EventTarget} node Element to remove event listener from.
-* @param {string} eventName Name of event to stop listening to.
-* @param {string} methodName Name of handler method on `this` to not call
-     anymore.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.unlisten = function(node, eventName, methodName){};
-/**
-* @override
-* @param {string=} direction Direction to allow scrolling
-Defaults to `all`.
-* @param {Element=} node Element to apply scroll direction setting.
-Defaults to `this`.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.setScrollDirection = function(direction, node){};
-/**
-* @override
-* @param {string} slctr Selector to run on this local DOM scope
-* @return {Element}
-*/
-Polymer_LegacyElementMixin.prototype.$$ = function(slctr){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.distributeContent = function(){};
-/**
-* @override
-* @return {!Array.<!Node>}
-*/
-Polymer_LegacyElementMixin.prototype.getEffectiveChildNodes = function(){};
-/**
-* @override
-* @param {string} selector Selector to run.
-* @return {!Array.<!Node>}
-*/
-Polymer_LegacyElementMixin.prototype.queryDistributedElements = function(selector){};
-/**
-* @override
-* @return {!Array.<!Node>}
-*/
-Polymer_LegacyElementMixin.prototype.getEffectiveChildren = function(){};
-/**
-* @override
-* @return {string}
-*/
-Polymer_LegacyElementMixin.prototype.getEffectiveTextContent = function(){};
-/**
-* @override
-* @param {string} selector Selector to run.
-* @return {Node}
-*/
-Polymer_LegacyElementMixin.prototype.queryEffectiveChildren = function(selector){};
-/**
-* @override
-* @param {string} selector Selector to run.
-* @return {!Array.<!Node>}
-*/
-Polymer_LegacyElementMixin.prototype.queryAllEffectiveChildren = function(selector){};
-/**
-* @override
-* @param {string=} slctr CSS selector to choose the desired
-  `<slot>`.  Defaults to `content`.
-* @return {!Array.<!Node>}
-*/
-Polymer_LegacyElementMixin.prototype.getContentChildNodes = function(slctr){};
-/**
-* @override
-* @param {string=} slctr CSS selector to choose the desired
-  `<content>`.  Defaults to `content`.
-* @return {!Array.<!HTMLElement>}
-*/
-Polymer_LegacyElementMixin.prototype.getContentChildren = function(slctr){};
-/**
-* @override
-* @param {?Node} node The element to be checked.
-* @return {boolean}
-*/
-Polymer_LegacyElementMixin.prototype.isLightDescendant = function(node){};
-/**
-* @override
-* @param {!Element} node The element to be checked.
-* @return {boolean}
-*/
-Polymer_LegacyElementMixin.prototype.isLocalDescendant = function(node){};
-/**
-* @override
-* @param {*} container Unused
-* @param {*} shouldObserve Unused
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.scopeSubtree = function(container, shouldObserve){};
-/**
-* @override
-* @param {string} property The css property name.
-* @return {string}
-*/
-Polymer_LegacyElementMixin.prototype.getComputedStyleValue = function(property){};
-/**
-* @override
-* @param {string} jobName String to identify the debounce job.
-* @param {function (): void} callback Function that is called (with `this`
-  context) when the wait time elapses.
-* @param {number=} wait Optional wait time in milliseconds (ms) after the
-  last signal that must elapse before invoking `callback`
-* @return {!Object}
-*/
-Polymer_LegacyElementMixin.prototype.debounce = function(jobName, callback, wait){};
-/**
-* @override
-* @param {string} jobName The name of the debouncer started with `debounce`
-* @return {boolean}
-*/
-Polymer_LegacyElementMixin.prototype.isDebouncerActive = function(jobName){};
-/**
-* @override
-* @param {string} jobName The name of the debouncer started with `debounce`
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.flushDebouncer = function(jobName){};
-/**
-* @override
-* @param {string} jobName The name of the debouncer started with `debounce`
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.cancelDebouncer = function(jobName){};
-/**
-* @override
-* @param {!Function} callback The callback function to run, bound to
-    `this`.
-* @param {number=} waitTime Time to wait before calling the
-  `callback`.  If unspecified or 0, the callback will be run at microtask
-  timing (before paint).
-* @return {number}
-*/
-Polymer_LegacyElementMixin.prototype.async = function(callback, waitTime){};
-/**
-* @override
-* @param {number} handle Handle returned from original `async` call to
-  cancel.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.cancelAsync = function(handle){};
-/**
-* @override
-* @param {string} tag HTML element tag to create.
-* @param {Object=} props Object of properties to configure on the
-   instance.
-* @return {!Element}
-*/
-Polymer_LegacyElementMixin.prototype.create = function(tag, props){};
-/**
-* @override
-* @param {string} selector Selector to test.
-* @param {!Element=} node Element to test the selector against.
-* @return {boolean}
-*/
-Polymer_LegacyElementMixin.prototype.elementMatches = function(selector, node){};
-/**
-* @override
-* @param {string} name HTML attribute name
-* @param {boolean=} bool Boolean to force the attribute on or off.
-   When unspecified, the state of the attribute will be reversed.
-* @return {boolean}
-*/
-Polymer_LegacyElementMixin.prototype.toggleAttribute = function(name, bool){};
-/**
-* @override
-* @param {string} name CSS class name
-* @param {boolean=} bool Boolean to force the class on or off.
-   When unspecified, the state of the class will be reversed.
-* @param {Element=} node Node to target.  Defaults to `this`.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.toggleClass = function(name, bool, node){};
-/**
-* @override
-* @param {string} transformText Transform setting.
-* @param {Element=} node Element to apply the transform to.
-Defaults to `this`
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.transform = function(transformText, node){};
-/**
-* @override
-* @param {number} x X offset.
-* @param {number} y Y offset.
-* @param {number} z Z offset.
-* @param {Element=} node Element to apply the transform to.
-Defaults to `this`.
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype.translate3d = function(x, y, z, node){};
-/**
-* @override
-* @param {(string | !Array.<(number | string)>)} arrayOrPath Path to array from
-    which to remove the item
-  (or the array itself).
-* @param {*} item Item to remove.
-* @return {Array}
-*/
-Polymer_LegacyElementMixin.prototype.arrayDelete = function(arrayOrPath, item){};
-/**
-* @override
-* @param {string} level One of 'log', 'warn', 'error'
-* @param {Array} args Array of strings or objects to log
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype._logger = function(level, args){};
-/**
-* @override
-* @param {...*} args Array of strings or objects to log
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype._log = function(args){};
-/**
-* @override
-* @param {...*} args Array of strings or objects to log
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype._warn = function(args){};
-/**
-* @override
-* @param {...*} args Array of strings or objects to log
-* @return {void}
-*/
-Polymer_LegacyElementMixin.prototype._error = function(args){};
-/**
-* @override
-* @param {string} methodName Method name to associate with message
-* @param {...*} args Array of strings or objects to log
-* @return {Array}
-*/
-Polymer_LegacyElementMixin.prototype._logf = function(methodName, args){};
-/**
-* @interface
-*/
-function Polymer_MutableData(){}
-/**
-* @param {string} property Property name
-* @param {*} value New property value
-* @param {*} old Previous property value
-* @return {boolean}
-*/
-Polymer_MutableData.prototype._shouldPropertyChange = function(property, value, old){};
-/**
-* @interface
-*/
-function Polymer_OptionalMutableData(){}
-/** @type {boolean | null | undefined} */
-Polymer_OptionalMutableData.prototype.mutableData;
-
-/**
-* @param {string} property Property name
-* @param {*} value New property value
-* @param {*} old Previous property value
-* @return {boolean}
-*/
-Polymer_OptionalMutableData.prototype._shouldPropertyChange = function(property, value, old){};
-/**
-* @interface
-* @extends {Polymer_ElementMixin}
-*/
-function Polymer_ArraySelectorMixin(){}
-/** @type {Array | null | undefined} */
-Polymer_ArraySelectorMixin.prototype.items;
-
-/** @type {boolean | null | undefined} */
-Polymer_ArraySelectorMixin.prototype.multi;
-
-/** @type {(?Object | ?Array.<!Object>)} */
-Polymer_ArraySelectorMixin.prototype.selected;
-
-/** @type {?Object} */
-Polymer_ArraySelectorMixin.prototype.selectedItem;
-
-/** @type {boolean | null | undefined} */
-Polymer_ArraySelectorMixin.prototype.toggle;
-
-/**
-* @override
-* @return {void}
-*/
-Polymer_ArraySelectorMixin.prototype.clearSelection = function(){};
-/**
-* @override
-* @param {*} item Item from `items` array to test
-* @return {boolean}
-*/
-Polymer_ArraySelectorMixin.prototype.isSelected = function(item){};
-/**
-* @override
-* @param {number} idx Index from `items` array to test
-* @return {boolean}
-*/
-Polymer_ArraySelectorMixin.prototype.isIndexSelected = function(idx){};
-/**
-* @override
-* @param {*} item Item from `items` array to deselect
-* @return {void}
-*/
-Polymer_ArraySelectorMixin.prototype.deselect = function(item){};
-/**
-* @override
-* @param {number} idx Index from `items` array to deselect
-* @return {void}
-*/
-Polymer_ArraySelectorMixin.prototype.deselectIndex = function(idx){};
-/**
-* @override
-* @param {*} item Item from `items` array to select
-* @return {void}
-*/
-Polymer_ArraySelectorMixin.prototype.select = function(item){};
-/**
-* @override
-* @param {number} idx Index from `items` array to select
-* @return {void}
-*/
-Polymer_ArraySelectorMixin.prototype.selectIndex = function(idx){};
-/**
-* @interface
-* @extends {Polymer_PropertyEffects}
-*/
-function Polymer_StrictBindingParser(){}
-/**
-* @param {string} text Text to parse from attribute or textContent
-* @param {Object} templateInfo Current template metadata
-* @return {Array.<!BindingPart>}
-*/
-Polymer_StrictBindingParser._parseBindings = function(text, templateInfo){};
-/**
-* @interface
-* @extends {Polymer_ElementMixin}
-*/
-function Polymer_DisableUpgradeMixin(){}
-/**
-* @override
-* @return {void}
-*/
-Polymer_DisableUpgradeMixin.prototype._initializeProperties = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_DisableUpgradeMixin.prototype._enableProperties = function(){};
-/**
-* @override
-* @param {string} name Attribute name.
-* @param {?string} old The previous value for the attribute.
-* @param {?string} value The new value for the attribute.
-* @param {?string=} namespace The XML namespace for the attribute.
-* @return {undefined}
-*/
-Polymer_DisableUpgradeMixin.prototype.attributeChangedCallback = function(name, old, value, namespace){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_DisableUpgradeMixin.prototype.connectedCallback = function(){};
-/**
-* @override
-* @return {void}
-*/
-Polymer_DisableUpgradeMixin.prototype.disconnectedCallback = function(){};
-/**
-* @interface
-*/
-function Polymer_LegacyDataMixin(){}
-/**
-* @interface
-*/
-function Polymer_TemplatizeMixin(){}
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-dom-api-externs.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-dom-api-externs.js
deleted file mode 100644
index 9b52e339..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-dom-api-externs.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
- * @externs
- * @fileoverview Externs for PolymerDomApi for backwards compatibility with
- * the Polymer 1 externs.
- */
-
-/**
- * A Polymer DOM API for manipulating DOM such that local DOM and light DOM
- * trees are properly maintained.
- *
- * This type exists only to provide compatibility between compiled hybrid
- * Polymer V1 and V2 code. Polymer V2 only code should simply use the DomApi
- * class type.
- *
- * @interface
- */
-var PolymerDomApi = function() {};
-
-/**
- * @param {?Node} node
- * @return {boolean}
- */
-PolymerDomApi.prototype.deepContains = function(node) {};
-
-/** @param {!Node} node */
-PolymerDomApi.prototype.appendChild = function(node) {};
-
-/**
- * @param {!Node} oldNode
- * @param {!Node} newNode
- */
-PolymerDomApi.prototype.replaceChild = function(oldNode, newNode) {};
-
-/**
- * @param {!Node} node
- * @param {?Node} beforeNode
- */
-PolymerDomApi.prototype.insertBefore = function(node, beforeNode) {};
-
-/** @param {!Node} node */
-PolymerDomApi.prototype.removeChild = function(node) {};
-
-/** @type {!Array<!HTMLElement>|!NodeList<!HTMLElement>} */
-PolymerDomApi.prototype.children;
-
-/** @type {!Array<!Node>|!NodeList<!Node>} */
-PolymerDomApi.prototype.childNodes;
-
-/** @type {?Node} */
-PolymerDomApi.prototype.parentNode;
-
-/** @type {?Node} */
-PolymerDomApi.prototype.firstChild;
-
-/** @type {?Node} */
-PolymerDomApi.prototype.lastChild;
-
-/** @type {?HTMLElement} */
-PolymerDomApi.prototype.firstElementChild;
-
-/** @type {?HTMLElement} */
-PolymerDomApi.prototype.lastElementChild;
-
-/** @type {?Node} */
-PolymerDomApi.prototype.previousSibling;
-
-/** @type {?Node} */
-PolymerDomApi.prototype.nextSibling;
-
-/** @type {?HTMLElement} */
-PolymerDomApi.prototype.previousElementSibling;
-
-/** @type {?HTMLElement} */
-PolymerDomApi.prototype.nextElementSibling;
-
-/** @type {string} */
-PolymerDomApi.prototype.textContent;
-
-/** @type {string} */
-PolymerDomApi.prototype.innerHTML;
-
-/** @type {?HTMLElement} */
-PolymerDomApi.prototype.activeElement;
-
-/**
- * @param {string} selector
- * @return {?Element}
- */
-PolymerDomApi.prototype.querySelector = function(selector) {};
-
-/**
- * @param {string} selector
- * @return {!Array<!Element>|!NodeList<!Element>}
- */
-PolymerDomApi.prototype.querySelectorAll = function(selector) {};
-
-/** @return {!Array<!Node>} */
-PolymerDomApi.prototype.getDistributedNodes = function() {};
-
-/** @return {!Array<!Node>} */
-PolymerDomApi.prototype.getDestinationInsertionPoints = function() {};
-
-/** @return {?Node} */
-PolymerDomApi.prototype.getOwnerRoot = function() {};
-
-/**
- * @param {string} attribute
- * @param {string} value
- */
-PolymerDomApi.prototype.setAttribute = function(attribute, value) {};
-
-/** @param {string} attribute */
-PolymerDomApi.prototype.removeAttribute = function(attribute) {};
-
-/**
- * @typedef {function(!PolymerDomApi.ObserveInfo)}
- */
-PolymerDomApi.ObserveCallback;
-
-/**
- * @typedef {{
- *   target: !Node,
- *   addedNodes: !Array<!Node>,
- *   removedNodes: !Array<!Node>
- * }}
- */
-PolymerDomApi.ObserveInfo;
-
-/**
- * A virtual type for observer callback handles.
- *
- * @interface
- */
-PolymerDomApi.ObserveHandle = function() {};
-
-/**
- * @return {void}
- */
-PolymerDomApi.ObserveHandle.prototype.disconnect = function() {};
-
-/**
- * Notifies callers about changes to the element's effective child nodes,
- * the same list as returned by `getEffectiveChildNodes`.
- *
- * @param {!PolymerDomApi.ObserveCallback} callback The supplied callback
- * is called with an `info` argument which is an object that provides
- * the `target` on which the changes occurred, a list of any nodes
- * added in the `addedNodes` array, and nodes removed in the
- * `removedNodes` array.
- *
- * @return {!PolymerDomApi.ObserveHandle} Handle which is the argument to
- * `unobserveNodes`.
- */
-PolymerDomApi.prototype.observeNodes = function(callback) {};
-
-/**
- * Stops observing changes to the element's effective child nodes.
- *
- * @param {!PolymerDomApi.ObserveHandle} handle The handle for the
- * callback that should no longer receive notifications. This
- * handle is returned from `observeNodes`.
- */
-PolymerDomApi.prototype.unobserveNodes = function(handle) {};
-
-/** @type {?DOMTokenList} */
-PolymerDomApi.prototype.classList;
-
-/**
- * @param {string} selector
- * @return {!Array<!HTMLElement>}
- */
-PolymerDomApi.prototype.queryDistributedElements = function(selector) {};
-
-/**
- * Returns a list of effective child nodes for this element.
- *
- * @return {!Array<!HTMLElement>}
- */
-PolymerDomApi.prototype.getEffectiveChildNodes = function() {};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-externs.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-externs.js
deleted file mode 100644
index 0c7b8ea4..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-externs.js
+++ /dev/null
@@ -1,216 +0,0 @@
-/**
- * @fileoverview Externs for Polymer Pass and external Polymer API
- * @externs
- *
- * @license
- * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
- * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
- * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
- * Code distributed by Google as part of the polymer project is also
- * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
- */
-
-/* eslint-disable */
-
-/**
- * @typedef {{
- *   type: !Function,
- *   value: (* | undefined),
- *   readOnly: (boolean | undefined),
- *   computed: (string | undefined),
- *   reflectToAttribute: (boolean | undefined),
- *   notify: (boolean | undefined),
- *   observer: (string | function(this:?, ?, ?) | undefined)
- * }}
- */
-let PolymerElementPropertiesMeta;
-
-/**
- * @typedef {Object<string, !Function|!PolymerElementPropertiesMeta>}
- */
-let PolymerElementProperties;
-
-/** @record */
-let PolymerInit = function() {};
-/** @type {string} */
-PolymerInit.prototype.is;
-/** @type {(string | undefined)} */
-PolymerInit.prototype.extends;
-/** @type {(!PolymerElementProperties | undefined)} */
-PolymerInit.prototype.properties;
-/** @type {(!Array<string> | undefined)} */
-PolymerInit.prototype.observers;
-/** @type {(!HTMLTemplateElement | string | undefined)} */
-PolymerInit.prototype.template;
-/** @type {(!Object<string, *> | undefined)} */
-PolymerInit.prototype.hostAttributes;
-/** @type {(!Object<string, string> | undefined)} */
-PolymerInit.prototype.listeners;
-/** @type {(!Object| !Array<!Object> | undefined)} */
-PolymerInit.prototype.behaviors;
-
-/** @record */
-let PolymerElementConstructor = function () {};
-/** @type {(string | undefined)} */
-PolymerElementConstructor.is;
-/** @type {(string | undefined)} */
-PolymerElementConstructor.extends;
-/** @type {(!PolymerElementProperties | undefined)} */
-PolymerElementConstructor.properties;
-/** @type {(!Array<string> | undefined)} */
-PolymerElementConstructor.observers;
-/** @type {(!HTMLTemplateElement | string | undefined)} */
-PolymerElementConstructor.template;
-
-/** @interface */
-let PropertiesMixinConstructor = function () {};
-/** @type {(!PolymerElementProperties | undefined)} */
-PropertiesMixinConstructor.prototype.properties;
-/** @return {void} */
-PropertiesMixinConstructor.prototype.finalize = function() {};
-
-/**
- * @param {!PolymerInit} init
- * @return {!function(new:HTMLElement)}
- */
-function Polymer(init){}
-
-/**
- * @type {(function(*,string,string,Node):*)|undefined}
- */
-Polymer.sanitizeDOMValue;
-
-/**
- * @type {boolean}
- */
-Polymer.passiveTouchGestures;
-
-/**
- * @type {boolean}
- */
-Polymer.strictTemplatePolicy;
-
-/**
- * @type {boolean}
- */
-Polymer.allowTemplateFromDomModule;
-
-/**
- * @type {string}
- */
-Polymer.rootPath;
-
-/**
- * @param {string} string
- * @param {Object} obj
- * @return {string}
- */
-function JSCompiler_renameProperty(string, obj) {}
-
-/** @record */
-function PolymerTelemetry() {}
-/** @type {number} */
-PolymerTelemetry.instanceCount;
-/** @type {function():void} */
-PolymerTelemetry.incrementInstanceCount;
-/** @type {Array<HTMLElement>} */
-PolymerTelemetry.registrations;
-/** @type {function(HTMLElement)} */
-PolymerTelemetry._regLog;
-/** @type {function(HTMLElement)} */
-PolymerTelemetry.register;
-/** @type {function(HTMLElement)} */
-PolymerTelemetry.dumpRegistrations;
-
-/** @type {PolymerTelemetry} */
-Polymer.telemetry;
-
-/** @type {string} */
-Polymer.version;
-
-/** @type {boolean} */
-Polymer.legacyOptimizations;
-
-/** @type {boolean} */
-Polymer.syncInitialRender;
-
-// nb. This is explicitly 'var', as Closure Compiler checks that this is the case.
-/**
- * @constructor
- * @extends {HTMLElement}
- * @implements {Polymer_LegacyElementMixin}
- */
-var PolymerElement = function() {};
-
-/**
- * On create callback.
- * @override
- */
-PolymerElement.prototype.created = function() {};
-/**
- * On ready callback.
- * @override
- */
-PolymerElement.prototype.ready = function() {};
-/** On before register callback. */
-PolymerElement.prototype.beforeRegister = function() {};
-/** On registered callback. */
-PolymerElement.prototype.registered = function() {};
-/**
- * On attached to the DOM callback.
- * @override
- */
-PolymerElement.prototype.attached = function() {};
-/**
- * On detached from the DOM callback.
- * @override
- */
-PolymerElement.prototype.detached = function() {};
-
-/**
- * @typedef {{
- *   index: number,
- *   removed: !Array,
- *   addedCount: number,
- *   object: !Array,
- *   type: string,
- * }}
- */
-var PolymerSplice;
-/**
- * @typedef {{
- *   indexSplices: ?Array<!PolymerSplice>,
- * }}
- */
-var PolymerSpliceChange;
-
-/**
- * The type of the object received by an observer function when deep
- * sub-property observation is enabled. See:
- * https://www.polymer-project.org/2.0/docs/devguide/observers#deep-observation
- *
- * @typedef {{
- *   path: string,
- *   value: (?Object|undefined),
- *   base: (?Object|undefined)
- * }}
- */
-var PolymerDeepPropertyChange;
-
-/**
- * Event object for events dispatched by children of a dom-repeat template.
- * @see https://www.polymer-project.org/2.0/docs/devguide/templates#handling-events
- * @extends {Event}
- * @constructor
- * @template T
- */
-var DomRepeatEvent = function() {};
-
-/**
- * @type {{
- *   index: number,
- *   item: T
- * }}
- */
-DomRepeatEvent.prototype.model;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-iconset-externs.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-iconset-externs.js
deleted file mode 100644
index 916fee8..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-iconset-externs.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * @externs
- * @fileoverview Externs for Polymer.Iconset.
- */
-
-/**
- * The interface that iconsets should obey. Iconsets are registered by setting
- * their name in the IronMeta 'iconset' db, and a value of type Polymer.Iconset.
- *
- * Used by iron-icon but needs to live here since iron-icon, iron-iconset, etc don't
- * depend on each other at all and talk only through iron-meta.
- *
- * @interface
- */
-Polymer.Iconset = function() {};
-
-/**
- * Applies an icon to the given element as a css background image. This
- * method does not size the element, and it's usually necessary to set
- * the element's height and width so that the background image is visible.
- *
- * @param {Element} element The element to which the icon is applied.
- * @param {string} icon The name of the icon to apply.
- * @param {string=} theme (optional) The name or index of the icon to apply.
- * @param {number=} scale (optional, defaults to 1) Icon scaling factor.
- */
-Polymer.Iconset.prototype.applyIcon = function(
-      element, icon, theme, scale) {};
-
-/**
- * Remove an icon from the given element by undoing the changes effected
- * by `applyIcon`.
- *
- * @param {Element} element The element from which the icon is removed.
- */
-Polymer.Iconset.prototype.removeIcon = function(element) {};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-shared-types.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-shared-types.js
deleted file mode 100644
index fd954c11..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-shared-types.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/**
- * @fileoverview Internal shared types for Polymer
- * @externs
- *
- * @license
- * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
- * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
- * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
- * Code distributed by Google as part of the polymer project is also
- * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
- */
-
-/* eslint-disable no-unused-vars, strict, valid-jsdoc */
-
-/**
- * @constructor
- * @extends {DocumentFragment}
- */
-function StampedTemplate() { }
-/** @type {boolean} */
-StampedTemplate.prototype.__noInsertionPoint;
-/** @type {!Array<!Node>} */
-StampedTemplate.prototype.nodeList;
-/** @type {!Object<string, !Element>} */
-StampedTemplate.prototype.$;
-/** @type {!TemplateInfo | undefined} */
-StampedTemplate.prototype.templateInfo;
-
-/** @interface */
-function NodeInfo() { }
-/** @type {string} */
-NodeInfo.prototype.id;
-/** @type {!Array<{name: string, value: string}>}*/
-NodeInfo.prototype.events;
-/** @type {boolean} */
-NodeInfo.prototype.hasInsertionPoint;
-/** @type {!TemplateInfo} */
-NodeInfo.prototype.templateInfo;
-/** @type {!NodeInfo} */
-NodeInfo.prototype.parentInfo;
-/** @type {number} */
-NodeInfo.prototype.parentIndex;
-/** @type {number} */
-NodeInfo.prototype.infoIndex;
-/** @type {!Array<!Binding>} */
-NodeInfo.prototype.bindings;
-
-/** @interface */
-function TemplateInfo() { }
-/** @type {!Array<!NodeInfo>} */
-TemplateInfo.prototype.nodeInfoList;
-/** @type {!Array<!Node>} */
-TemplateInfo.prototype.nodeList;
-/** @type {boolean} */
-TemplateInfo.prototype.stripWhitespace;
-/** @type {boolean | undefined} */
-TemplateInfo.prototype.hasInsertionPoint;
-/** @type {!Object} */
-TemplateInfo.prototype.hostProps;
-/** @type {!Object} */
-TemplateInfo.prototype.propertyEffects;
-/** @type {TemplateInfo | undefined} */
-TemplateInfo.prototype.nextTemplateInfo;
-/** @type {TemplateInfo | undefined} */
-TemplateInfo.prototype.previousTemplateInfo;
-/** @type {!Array<!Node>} */
-TemplateInfo.prototype.childNodes;
-/** @type {boolean} */
-TemplateInfo.prototype.wasPreBound;
-
-/**
- * type for HTMLTemplateElement with `_templateInfo`
- *
- * @constructor
- * @extends {HTMLTemplateElement}
- */
-function HTMLTemplateElementWithInfo() { }
-/** @type {TemplateInfo} */
-HTMLTemplateElementWithInfo.prototype._templateInfo;
-
-/**
- * @typedef {{
- * literal: string,
- * compoundIndex: (number | undefined)
- * }}
- */
-let LiteralBindingPart;
-
-/**
- * @typedef {{
- * literal: boolean,
- * name: string,
- * value: (string | number),
- * rootProperty: (string | undefined),
- * structured: (boolean | undefined),
- * wildcard: (boolean | undefined)
- * }}
- */
-let MethodArg;
-
-/**
- * @typedef {{
- * methodName: string,
- * static: boolean,
- * args: !Array<!MethodArg>,
- * dynamicFn: (boolean | undefined),
- * }}
- */
-let MethodSignature;
-
-/**
- * @typedef {{
- * mode: string,
- * negate: boolean,
- * source: string,
- * dependencies: !Array<(!MethodArg|string)>,
- * customEvent: boolean,
- * signature: Object,
- * event: string
- * }}
- */
-let ExpressionBindingPart;
-
-/**
- * @typedef {LiteralBindingPart | ExpressionBindingPart}
- */
-let BindingPart;
-
-/**
- * @typedef {{
- * kind: string,
- * target: string,
- * parts: Array<!BindingPart>,
- * literal: (string | undefined),
- * isCompound: boolean,
- * listenerEvent: (string | undefined),
- * listenerNegate: (boolean | undefined)
- * }}
- */
-let Binding;
-
-/**
- * @typedef {{
- * path: string
- * }}
- */
-let PathInfo;
-
-/**
- * @typedef {{
- * forwardHostProp: (function(string, *) | undefined),
- * instanceProps: (Object | undefined),
- * mutableData: (boolean | undefined),
- * notifyInstanceProp: (function(*, string, *) | undefined),
- * parentModel: (boolean | undefined)
- * }}
- */
-let TemplatizeOptions;
-
-/** @record */
-function AsyncInterface(){}
-/** @type {function(!Function, number=): number} */
-AsyncInterface.prototype.run;
-/** @type {function(number): void} */
-AsyncInterface.prototype.cancel;
-
-/** @record */
-let GestureInfo = function(){};
-/** @type {string|undefined} */
-GestureInfo.prototype.state;
-/** @type {boolean|undefined} */
-GestureInfo.prototype.started;
-/** @type {!Array<?>|undefined} */
-GestureInfo.prototype.moves;
-/** @type {number|undefined} */
-GestureInfo.prototype.x;
-/** @type {number|undefined} */
-GestureInfo.prototype.y;
-/** @type {boolean|undefined} */
-GestureInfo.prototype.prevent;
-/** @type {function(?): void|undefined} */
-GestureInfo.prototype.addMove;
-/** @type {null|undefined} */
-GestureInfo.prototype.movefn;
-/** @type {null|undefined} */
-GestureInfo.prototype.upFn;
-
-/** @record */
-let GestureRecognizer = function(){};
-/** @type {string} */
-GestureRecognizer.prototype.name;
-/** @type {!Array<string>} */
-GestureRecognizer.prototype.deps;
-/** @type {function(): void} */
-GestureRecognizer.prototype.reset;
-/** @type {function(MouseEvent): void | undefined} */
-GestureRecognizer.prototype.mousedown;
-/** @type {(function(MouseEvent): void | undefined)} */
-GestureRecognizer.prototype.mousemove;
-/** @type {(function(MouseEvent): void | undefined)} */
-GestureRecognizer.prototype.mouseup;
-/** @type {(function(TouchEvent): void | undefined)} */
-GestureRecognizer.prototype.touchstart;
-/** @type {(function(TouchEvent): void | undefined)} */
-GestureRecognizer.prototype.touchmove;
-/** @type {(function(TouchEvent): void | undefined)} */
-GestureRecognizer.prototype.touchend;
-/** @type {(function(MouseEvent): void | undefined)} */
-GestureRecognizer.prototype.click;
-/** @type {!GestureInfo} */
-GestureRecognizer.prototype.info;
-/** @type {!Array<string>} */
-GestureRecognizer.prototype.emits;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-types.html b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-types.html
deleted file mode 100644
index bc7abad..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-types.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!--
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--->
-<!-- internal shared types for closure pass -->
-<script src="polymer-internal-shared-types.js"></script>
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/webcomponents-externs.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/webcomponents-externs.js
deleted file mode 100644
index 1d796de..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/externs/webcomponents-externs.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * @fileoverview Externs for webcomponents polyfills
- * @externs
- *
- * @license
- * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
- * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
- * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
- * Code distributed by Google as part of the polymer project is also
- * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
- */
-/* eslint-disable */
-
-var HTMLImports = {
-  /**
-   * @param {function()} callback
-   */
-  whenReady(callback) {},
-  /**
-   * @param {!Node} element
-   * @return {?HTMLLinkElement|?Document|undefined}
-   */
-  importForElement(element) {}
-};
-
-window.HTMLImports = HTMLImports;
-
-var ShadyDOM = {
-  inUse: false,
-  flush() {},
-  /**
-   * @param {!Node} target
-   * @param {function(Array<MutationRecord>, MutationObserver)} callback
-   * @return {MutationObserver}
-   */
-  observeChildren(target, callback) {},
-  /**
-   * @param {MutationObserver} observer
-   */
-  unobserveChildren(observer) {},
-  /**
-   * @param {Node} node
-   */
-  patch(node) {},
-  /**
-   * @param {!ShadowRoot} shadowroot
-   */
-  flushInitial(shadowroot) {}
-};
-
-window.ShadyDOM = ShadyDOM;
-
-var WebComponents = {};
-window.WebComponents = WebComponents;
-
-/** @type {Element} */
-HTMLElement.prototype._activeElement;
-
-/**
- * @param {HTMLTemplateElement} template
- */
-HTMLTemplateElement.decorate = function(template){};
-
-/**
- * @param {function(function())} cb callback
- */
-CustomElementRegistry.prototype.polyfillWrapFlushCallback = function(cb){};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/array-selector.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/array-selector.js
deleted file mode 100644
index 158d242c..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/array-selector.js
+++ /dev/null
@@ -1,434 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import { PolymerElement } from '../../polymer-element.js';
-
-import { dedupingMixin } from '../utils/mixin.js';
-import { calculateSplices } from '../utils/array-splice.js';
-import { ElementMixin } from '../mixins/element-mixin.js';
-
-/**
- * Element mixin for recording dynamic associations between item paths in a
- * master `items` array and a `selected` array such that path changes to the
- * master array (at the host) element or elsewhere via data-binding) are
- * correctly propagated to items in the selected array and vice-versa.
- *
- * The `items` property accepts an array of user data, and via the
- * `select(item)` and `deselect(item)` API, updates the `selected` property
- * which may be bound to other parts of the application, and any changes to
- * sub-fields of `selected` item(s) will be kept in sync with items in the
- * `items` array.  When `multi` is false, `selected` is a property
- * representing the last selected item.  When `multi` is true, `selected`
- * is an array of multiply selected items.
- *
- * @polymer
- * @mixinFunction
- * @appliesMixin ElementMixin
- * @summary Element mixin for recording dynamic associations between item paths in a
- * master `items` array and a `selected` array
- */
-let ArraySelectorMixin = dedupingMixin(superClass => {
-
-  /**
-   * @constructor
-   * @implements {Polymer_ElementMixin}
-   * @private
-   */
-  let elementBase = ElementMixin(superClass);
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_ArraySelectorMixin}
-   * @unrestricted
-   */
-  class ArraySelectorMixin extends elementBase {
-
-    static get properties() {
-      return {
-
-        /**
-         * An array containing items from which selection will be made.
-         */
-        items: {
-          type: Array,
-        },
-
-        /**
-         * When `true`, multiple items may be selected at once (in this case,
-         * `selected` is an array of currently selected items).  When `false`,
-         * only one item may be selected at a time.
-         */
-        multi: {
-          type: Boolean,
-          value: false,
-        },
-
-        /**
-         * When `multi` is true, this is an array that contains any selected.
-         * When `multi` is false, this is the currently selected item, or `null`
-         * if no item is selected.
-         * @type {?Object|?Array<!Object>}
-         */
-        selected: {type: Object, notify: true},
-
-        /**
-         * When `multi` is false, this is the currently selected item, or `null`
-         * if no item is selected.
-         * @type {?Object}
-         */
-        selectedItem: {type: Object, notify: true},
-
-        /**
-         * When `true`, calling `select` on an item that is already selected
-         * will deselect the item.
-         */
-        toggle: {type: Boolean, value: false}
-
-      };
-    }
-
-    static get observers() {
-      return ['__updateSelection(multi, items.*)'];
-    }
-
-    constructor() {
-      super();
-      this.__lastItems = null;
-      this.__lastMulti = null;
-      this.__selectedMap = null;
-    }
-
-    __updateSelection(multi, itemsInfo) {
-      let path = itemsInfo.path;
-      if (path == JSCompiler_renameProperty('items', this)) {
-        // Case 1 - items array changed, so diff against previous array and
-        // deselect any removed items and adjust selected indices
-        let newItems = itemsInfo.base || [];
-        let lastItems = this.__lastItems;
-        let lastMulti = this.__lastMulti;
-        if (multi !== lastMulti) {
-          this.clearSelection();
-        }
-        if (lastItems) {
-          let splices = calculateSplices(newItems, lastItems);
-          this.__applySplices(splices);
-        }
-        this.__lastItems = newItems;
-        this.__lastMulti = multi;
-      } else if (itemsInfo.path == `${JSCompiler_renameProperty('items', this)}.splices`) {
-        // Case 2 - got specific splice information describing the array mutation:
-        // deselect any removed items and adjust selected indices
-        this.__applySplices(itemsInfo.value.indexSplices);
-      } else {
-        // Case 3 - an array element was changed, so deselect the previous
-        // item for that index if it was previously selected
-        let part = path.slice(`${JSCompiler_renameProperty('items', this)}.`.length);
-        let idx = parseInt(part, 10);
-        if ((part.indexOf('.') < 0) && part == idx) {
-          this.__deselectChangedIdx(idx);
-        }
-      }
-    }
-
-    __applySplices(splices) {
-      let selected = this.__selectedMap;
-      // Adjust selected indices and mark removals
-      for (let i=0; i<splices.length; i++) {
-        let s = splices[i];
-        selected.forEach((idx, item) => {
-          if (idx < s.index) {
-            // no change
-          } else if (idx >= s.index + s.removed.length) {
-            // adjust index
-            selected.set(item, idx + s.addedCount - s.removed.length);
-          } else {
-            // remove index
-            selected.set(item, -1);
-          }
-        });
-        for (let j=0; j<s.addedCount; j++) {
-          let idx = s.index + j;
-          if (selected.has(this.items[idx])) {
-            selected.set(this.items[idx], idx);
-          }
-        }
-      }
-      // Update linked paths
-      this.__updateLinks();
-      // Remove selected items that were removed from the items array
-      let sidx = 0;
-      selected.forEach((idx, item) => {
-        if (idx < 0) {
-          if (this.multi) {
-            this.splice(JSCompiler_renameProperty('selected', this), sidx, 1);
-          } else {
-            this.selected = this.selectedItem = null;
-          }
-          selected.delete(item);
-        } else {
-          sidx++;
-        }
-      });
-    }
-
-    __updateLinks() {
-      this.__dataLinkedPaths = {};
-      if (this.multi) {
-        let sidx = 0;
-        this.__selectedMap.forEach(idx => {
-          if (idx >= 0) {
-            this.linkPaths(
-                `${JSCompiler_renameProperty('items', this)}.${idx}`,
-                `${JSCompiler_renameProperty('selected', this)}.${sidx++}`);
-          }
-        });
-      } else {
-        this.__selectedMap.forEach(idx => {
-          this.linkPaths(
-              JSCompiler_renameProperty('selected', this),
-              `${JSCompiler_renameProperty('items', this)}.${idx}`);
-          this.linkPaths(
-              JSCompiler_renameProperty('selectedItem', this),
-              `${JSCompiler_renameProperty('items', this)}.${idx}`);
-        });
-      }
-    }
-
-    /**
-     * Clears the selection state.
-     * @override
-     * @return {void}
-     */
-    clearSelection() {
-      // Unbind previous selection
-      this.__dataLinkedPaths = {};
-      // The selected map stores 3 pieces of information:
-      // key: items array object
-      // value: items array index
-      // order: selected array index
-      this.__selectedMap = new Map();
-      // Initialize selection
-      this.selected = this.multi ? [] : null;
-      this.selectedItem = null;
-    }
-
-    /**
-     * Returns whether the item is currently selected.
-     *
-     * @override
-     * @param {*} item Item from `items` array to test
-     * @return {boolean} Whether the item is selected
-     */
-    isSelected(item) {
-      return this.__selectedMap.has(item);
-    }
-
-    /**
-     * Returns whether the item is currently selected.
-     *
-     * @override
-     * @param {number} idx Index from `items` array to test
-     * @return {boolean} Whether the item is selected
-     */
-    isIndexSelected(idx) {
-      return this.isSelected(this.items[idx]);
-    }
-
-    __deselectChangedIdx(idx) {
-      let sidx = this.__selectedIndexForItemIndex(idx);
-      if (sidx >= 0) {
-        let i = 0;
-        this.__selectedMap.forEach((idx, item) => {
-          if (sidx == i++) {
-            this.deselect(item);
-          }
-        });
-      }
-    }
-
-    __selectedIndexForItemIndex(idx) {
-      let selected = this.__dataLinkedPaths[`${JSCompiler_renameProperty('items', this)}.${idx}`];
-      if (selected) {
-        return parseInt(selected.slice(`${JSCompiler_renameProperty('selected', this)}.`.length), 10);
-      }
-    }
-
-    /**
-     * Deselects the given item if it is already selected.
-     *
-     * @override
-     * @param {*} item Item from `items` array to deselect
-     * @return {void}
-     */
-    deselect(item) {
-      let idx = this.__selectedMap.get(item);
-      if (idx >= 0) {
-        this.__selectedMap.delete(item);
-        let sidx;
-        if (this.multi) {
-          sidx = this.__selectedIndexForItemIndex(idx);
-        }
-        this.__updateLinks();
-        if (this.multi) {
-          this.splice(JSCompiler_renameProperty('selected', this), sidx, 1);
-        } else {
-          this.selected = this.selectedItem = null;
-        }
-      }
-    }
-
-    /**
-     * Deselects the given index if it is already selected.
-     *
-     * @override
-     * @param {number} idx Index from `items` array to deselect
-     * @return {void}
-     */
-    deselectIndex(idx) {
-      this.deselect(this.items[idx]);
-    }
-
-    /**
-     * Selects the given item.  When `toggle` is true, this will automatically
-     * deselect the item if already selected.
-     *
-     * @override
-     * @param {*} item Item from `items` array to select
-     * @return {void}
-     */
-    select(item) {
-      this.selectIndex(this.items.indexOf(item));
-    }
-
-    /**
-     * Selects the given index.  When `toggle` is true, this will automatically
-     * deselect the item if already selected.
-     *
-     * @override
-     * @param {number} idx Index from `items` array to select
-     * @return {void}
-     */
-    selectIndex(idx) {
-      let item = this.items[idx];
-      if (!this.isSelected(item)) {
-        if (!this.multi) {
-          this.__selectedMap.clear();
-        }
-        this.__selectedMap.set(item, idx);
-        this.__updateLinks();
-        if (this.multi) {
-          this.push(JSCompiler_renameProperty('selected', this), item);
-        } else {
-          this.selected = this.selectedItem = item;
-        }
-      } else if (this.toggle) {
-        this.deselectIndex(idx);
-      }
-    }
-
-  }
-
-  return ArraySelectorMixin;
-
-});
-
-// export mixin
-export { ArraySelectorMixin };
-
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {Polymer_ArraySelectorMixin}
- * @private
- */
-let baseArraySelector = ArraySelectorMixin(PolymerElement);
-
-/**
- * Element implementing the `ArraySelector` mixin, which records
- * dynamic associations between item paths in a master `items` array and a
- * `selected` array such that path changes to the master array (at the host)
- * element or elsewhere via data-binding) are correctly propagated to items
- * in the selected array and vice-versa.
- *
- * The `items` property accepts an array of user data, and via the
- * `select(item)` and `deselect(item)` API, updates the `selected` property
- * which may be bound to other parts of the application, and any changes to
- * sub-fields of `selected` item(s) will be kept in sync with items in the
- * `items` array.  When `multi` is false, `selected` is a property
- * representing the last selected item.  When `multi` is true, `selected`
- * is an array of multiply selected items.
- *
- * Example:
- *
- * ```js
- * import {PolymerElement} from '@polymer/polymer';
- * import '@polymer/polymer/lib/elements/array-selector.js';
- *
- * class EmployeeList extends PolymerElement {
- *   static get _template() {
- *     return html`
- *         <div> Employee list: </div>
- *         <dom-repeat id="employeeList" items="{{employees}}">
- *           <template>
- *             <div>First name: <span>{{item.first}}</span></div>
- *               <div>Last name: <span>{{item.last}}</span></div>
- *               <button on-click="toggleSelection">Select</button>
- *           </template>
- *         </dom-repeat>
- *
- *         <array-selector id="selector"
- *                         items="{{employees}}"
- *                         selected="{{selected}}"
- *                         multi toggle></array-selector>
- *
- *         <div> Selected employees: </div>
- *         <dom-repeat items="{{selected}}">
- *           <template>
- *             <div>First name: <span>{{item.first}}</span></div>
- *             <div>Last name: <span>{{item.last}}</span></div>
- *           </template>
- *         </dom-repeat>`;
- *   }
- *   static get is() { return 'employee-list'; }
- *   static get properties() {
- *     return {
- *       employees: {
- *         value() {
- *           return [
- *             {first: 'Bob', last: 'Smith'},
- *             {first: 'Sally', last: 'Johnson'},
- *             ...
- *           ];
- *         }
- *       }
- *     };
- *   }
- *   toggleSelection(e) {
- *     const item = this.$.employeeList.itemForElement(e.target);
- *     this.$.selector.select(item);
- *   }
- * }
- * ```
- *
- * @polymer
- * @customElement
- * @extends {baseArraySelector}
- * @appliesMixin ArraySelectorMixin
- * @summary Custom element that links paths between an input `items` array and
- *   an output `selected` item or array based on calls to its selection API.
- */
-class ArraySelector extends baseArraySelector {
-  // Not needed to find template; can be removed once the analyzer
-  // can find the tag name from customElements.define call
-  static get is() { return 'array-selector'; }
-  static get template() { return null; }
-}
-customElements.define(ArraySelector.is, ArraySelector);
-export { ArraySelector };
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/custom-style.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/custom-style.js
deleted file mode 100644
index b58b320..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/custom-style.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../../../shadycss/entrypoints/custom-style-interface.js';
-
-import { cssFromModules } from '../utils/style-gather.js';
-
-const attr = 'include';
-
-const CustomStyleInterface = window.ShadyCSS.CustomStyleInterface;
-
-/**
- * Custom element for defining styles in the main document that can take
- * advantage of [shady DOM](https://github.com/webcomponents/shadycss) shims
- * for style encapsulation, custom properties, and custom mixins.
- *
- * - Document styles defined in a `<custom-style>` are shimmed to ensure they
- *   do not leak into local DOM when running on browsers without native
- *   Shadow DOM.
- * - Custom properties can be defined in a `<custom-style>`. Use the `html` selector
- *   to define custom properties that apply to all custom elements.
- * - Custom mixins can be defined in a `<custom-style>`, if you import the optional
- *   [apply shim](https://github.com/webcomponents/shadycss#about-applyshim)
- *   (`shadycss/apply-shim.html`).
- *
- * To use:
- *
- * - Import `custom-style.html`.
- * - Place a `<custom-style>` element in the main document, wrapping an inline `<style>` tag that
- *   contains the CSS rules you want to shim.
- *
- * For example:
- *
- * ```html
- * <!-- import apply shim--only required if using mixins -->
- * <link rel="import" href="bower_components/shadycss/apply-shim.html">
- * <!-- import custom-style element -->
- * <link rel="import" href="bower_components/polymer/lib/elements/custom-style.html">
- *
- * <custom-style>
- *   <style>
- *     html {
- *       --custom-color: blue;
- *       --custom-mixin: {
- *         font-weight: bold;
- *         color: red;
- *       };
- *     }
- *   </style>
- * </custom-style>
- * ```
- *
- * @customElement
- * @extends HTMLElement
- * @summary Custom element for defining styles in the main document that can
- *   take advantage of Polymer's style scoping and custom properties shims.
- */
-export class CustomStyle extends HTMLElement {
-  constructor() {
-    super();
-    this._style = null;
-    CustomStyleInterface.addCustomStyle(this);
-  }
-  /**
-   * Returns the light-DOM `<style>` child this element wraps.  Upon first
-   * call any style modules referenced via the `include` attribute will be
-   * concatenated to this element's `<style>`.
-   *
-   * @export
-   * @return {HTMLStyleElement} This element's light-DOM `<style>`
-   */
-  getStyle() {
-    if (this._style) {
-      return this._style;
-    }
-    const style = /** @type {HTMLStyleElement} */(this.querySelector('style'));
-    if (!style) {
-      return null;
-    }
-    this._style = style;
-    const include = style.getAttribute(attr);
-    if (include) {
-      style.removeAttribute(attr);
-      style.textContent = cssFromModules(include) + style.textContent;
-    }
-    /*
-    HTML Imports styling the main document are deprecated in Chrome
-    https://crbug.com/523952
-
-    If this element is not in the main document, then it must be in an HTML Import document.
-    In that case, move the custom style to the main document.
-
-    The ordering of `<custom-style>` should stay the same as when loaded by HTML Imports, but there may be odd
-    cases of ordering w.r.t the main document styles.
-    */
-    if (this.ownerDocument !== window.document) {
-      window.document.head.appendChild(this);
-    }
-    return this._style;
-  }
-}
-
-window.customElements.define('custom-style', CustomStyle);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-bind.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-bind.js
deleted file mode 100644
index e02d041a..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-bind.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-
-import { PropertyEffects } from '../mixins/property-effects.js';
-import { OptionalMutableData } from '../mixins/mutable-data.js';
-import { GestureEventListeners } from '../mixins/gesture-event-listeners.js';
-import { strictTemplatePolicy } from '../utils/settings.js';
-import { wrap } from '../utils/wrap.js';
-
-/**
- * @constructor
- * @extends {HTMLElement}
- * @implements {Polymer_PropertyEffects}
- * @implements {Polymer_OptionalMutableData}
- * @implements {Polymer_GestureEventListeners}
- * @private
- */
-const domBindBase =
-  GestureEventListeners(
-    OptionalMutableData(
-      PropertyEffects(HTMLElement)));
-
-/**
- * Custom element to allow using Polymer's template features (data binding,
- * declarative event listeners, etc.) in the main document without defining
- * a new custom element.
- *
- * `<template>` tags utilizing bindings may be wrapped with the `<dom-bind>`
- * element, which will immediately stamp the wrapped template into the main
- * document and bind elements to the `dom-bind` element itself as the
- * binding scope.
- *
- * @polymer
- * @customElement
- * @appliesMixin PropertyEffects
- * @appliesMixin OptionalMutableData
- * @appliesMixin GestureEventListeners
- * @extends {domBindBase}
- * @summary Custom element to allow using Polymer's template features (data
- *   binding, declarative event listeners, etc.) in the main document.
- */
-export class DomBind extends domBindBase {
-
-  static get observedAttributes() { return ['mutable-data']; }
-
-  constructor() {
-    super();
-    if (strictTemplatePolicy) {
-      throw new Error(`strictTemplatePolicy: dom-bind not allowed`);
-    }
-    this.root = null;
-    this.$ = null;
-    this.__children = null;
-  }
-
-  /**
-   * @override
-   * @return {void}
-   */
-  attributeChangedCallback() {
-    // assumes only one observed attribute
-    this.mutableData = true;
-  }
-
-  /**
-   * @override
-   * @return {void}
-   */
-  connectedCallback() {
-    this.style.display = 'none';
-    this.render();
-  }
-
-  /**
-   * @override
-   * @return {void}
-   */
-  disconnectedCallback() {
-    this.__removeChildren();
-  }
-
-  __insertChildren() {
-    wrap(wrap(this).parentNode).insertBefore(this.root, this);
-  }
-
-  __removeChildren() {
-    if (this.__children) {
-      for (let i=0; i<this.__children.length; i++) {
-        this.root.appendChild(this.__children[i]);
-      }
-    }
-  }
-
-  /**
-   * Forces the element to render its content. This is typically only
-   * necessary to call if HTMLImports with the async attribute are used.
-   * @return {void}
-   */
-  render() {
-    let template;
-    if (!this.__children) {
-      template = /** @type {HTMLTemplateElement} */(template || this.querySelector('template'));
-      if (!template) {
-        // Wait until childList changes and template should be there by then
-        let observer = new MutationObserver(() => {
-          template = /** @type {HTMLTemplateElement} */(this.querySelector('template'));
-          if (template) {
-            observer.disconnect();
-            this.render();
-          } else {
-            throw new Error('dom-bind requires a <template> child');
-          }
-        });
-        observer.observe(this, {childList: true});
-        return;
-      }
-      this.root = this._stampTemplate(
-        /** @type {!HTMLTemplateElement} */(template));
-      this.$ = this.root.$;
-      this.__children = [];
-      for (let n=this.root.firstChild; n; n=n.nextSibling) {
-        this.__children[this.__children.length] = n;
-      }
-      this._enableProperties();
-    }
-    this.__insertChildren();
-    this.dispatchEvent(new CustomEvent('dom-change', {
-      bubbles: true,
-      composed: true
-    }));
-  }
-
-}
-
-customElements.define('dom-bind', DomBind);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-if.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-if.js
deleted file mode 100644
index a13940a..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-if.js
+++ /dev/null
@@ -1,289 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import { PolymerElement } from '../../polymer-element.js';
-
-import { templatize } from '../utils/templatize.js';
-import { Debouncer } from '../utils/debounce.js';
-import { enqueueDebouncer, flush } from '../utils/flush.js';
-import { microTask } from '../utils/async.js';
-import { root } from '../utils/path.js';
-import { wrap } from '../utils/wrap.js';
-
-/**
- * The `<dom-if>` element will stamp a light-dom `<template>` child when
- * the `if` property becomes truthy, and the template can use Polymer
- * data-binding and declarative event features when used in the context of
- * a Polymer element's template.
- *
- * When `if` becomes falsy, the stamped content is hidden but not
- * removed from dom. When `if` subsequently becomes truthy again, the content
- * is simply re-shown. This approach is used due to its favorable performance
- * characteristics: the expense of creating template content is paid only
- * once and lazily.
- *
- * Set the `restamp` property to true to force the stamped content to be
- * created / destroyed when the `if` condition changes.
- *
- * @customElement
- * @polymer
- * @extends PolymerElement
- * @summary Custom element that conditionally stamps and hides or removes
- *   template content based on a boolean flag.
- */
-export class DomIf extends PolymerElement {
-
-  // Not needed to find template; can be removed once the analyzer
-  // can find the tag name from customElements.define call
-  static get is() { return 'dom-if'; }
-
-  static get template() { return null; }
-
-  static get properties() {
-
-    return {
-
-      /**
-       * Fired whenever DOM is added or removed/hidden by this template (by
-       * default, rendering occurs lazily).  To force immediate rendering, call
-       * `render`.
-       *
-       * @event dom-change
-       */
-
-      /**
-       * A boolean indicating whether this template should stamp.
-       */
-      if: {
-        type: Boolean,
-        observer: '__debounceRender'
-      },
-
-      /**
-       * When true, elements will be removed from DOM and discarded when `if`
-       * becomes false and re-created and added back to the DOM when `if`
-       * becomes true.  By default, stamped elements will be hidden but left
-       * in the DOM when `if` becomes false, which is generally results
-       * in better performance.
-       */
-      restamp: {
-        type: Boolean,
-        observer: '__debounceRender'
-      }
-
-    };
-
-  }
-
-  constructor() {
-    super();
-    this.__renderDebouncer = null;
-    this.__invalidProps = null;
-    this.__instance = null;
-    this._lastIf = false;
-    this.__ctor = null;
-    this.__hideTemplateChildren__ = false;
-  }
-
-  __debounceRender() {
-    // Render is async for 2 reasons:
-    // 1. To eliminate dom creation trashing if user code thrashes `if` in the
-    //    same turn. This was more common in 1.x where a compound computed
-    //    property could result in the result changing multiple times, but is
-    //    mitigated to a large extent by batched property processing in 2.x.
-    // 2. To avoid double object propagation when a bag including values bound
-    //    to the `if` property as well as one or more hostProps could enqueue
-    //    the <dom-if> to flush before the <template>'s host property
-    //    forwarding. In that scenario creating an instance would result in
-    //    the host props being set once, and then the enqueued changes on the
-    //    template would set properties a second time, potentially causing an
-    //    object to be set to an instance more than once.  Creating the
-    //    instance async from flushing data ensures this doesn't happen. If
-    //    we wanted a sync option in the future, simply having <dom-if> flush
-    //    (or clear) its template's pending host properties before creating
-    //    the instance would also avoid the problem.
-    this.__renderDebouncer = Debouncer.debounce(
-          this.__renderDebouncer
-        , microTask
-        , () => this.__render());
-    enqueueDebouncer(this.__renderDebouncer);
-  }
-
-  /**
-   * @override
-   * @return {void}
-   */
-  disconnectedCallback() {
-    super.disconnectedCallback();
-    const parent = wrap(this).parentNode;
-    if (!parent || (parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE &&
-        !wrap(parent).host)) {
-      this.__teardownInstance();
-    }
-  }
-
-  /**
-   * @override
-   * @return {void}
-   */
-  connectedCallback() {
-    super.connectedCallback();
-    this.style.display = 'none';
-    if (this.if) {
-      this.__debounceRender();
-    }
-  }
-
-  /**
-   * Forces the element to render its content. Normally rendering is
-   * asynchronous to a provoking change. This is done for efficiency so
-   * that multiple changes trigger only a single render. The render method
-   * should be called if, for example, template rendering is required to
-   * validate application state.
-   * @return {void}
-   */
-  render() {
-    flush();
-  }
-
-  __render() {
-    if (this.if) {
-      if (!this.__ensureInstance()) {
-        // No template found yet
-        return;
-      }
-      this._showHideChildren();
-    } else if (this.restamp) {
-      this.__teardownInstance();
-    }
-    if (!this.restamp && this.__instance) {
-      this._showHideChildren();
-    }
-    if (this.if != this._lastIf) {
-      this.dispatchEvent(new CustomEvent('dom-change', {
-        bubbles: true,
-        composed: true
-      }));
-      this._lastIf = this.if;
-    }
-  }
-
-  __ensureInstance() {
-    let parentNode = wrap(this).parentNode;
-    // Guard against element being detached while render was queued
-    if (parentNode) {
-      if (!this.__ctor) {
-        let template = /** @type {HTMLTemplateElement} */(wrap(this).querySelector('template'));
-        if (!template) {
-          // Wait until childList changes and template should be there by then
-          let observer = new MutationObserver(() => {
-            if (wrap(this).querySelector('template')) {
-              observer.disconnect();
-              this.__render();
-            } else {
-              throw new Error('dom-if requires a <template> child');
-            }
-          });
-          observer.observe(this, {childList: true});
-          return false;
-        }
-        this.__ctor = templatize(template, this, {
-          // dom-if templatizer instances require `mutable: true`, as
-          // `__syncHostProperties` relies on that behavior to sync objects
-          mutableData: true,
-          /**
-           * @param {string} prop Property to forward
-           * @param {*} value Value of property
-           * @this {DomIf}
-           */
-          forwardHostProp: function(prop, value) {
-            if (this.__instance) {
-              if (this.if) {
-                this.__instance.forwardHostProp(prop, value);
-              } else {
-                // If we have an instance but are squelching host property
-                // forwarding due to if being false, note the invalidated
-                // properties so `__syncHostProperties` can sync them the next
-                // time `if` becomes true
-                this.__invalidProps = this.__invalidProps || Object.create(null);
-                this.__invalidProps[root(prop)] = true;
-              }
-            }
-          }
-        });
-      }
-      if (!this.__instance) {
-        this.__instance = new this.__ctor();
-        wrap(parentNode).insertBefore(this.__instance.root, this);
-      } else {
-        this.__syncHostProperties();
-        let c$ = this.__instance.children;
-        if (c$ && c$.length) {
-          // Detect case where dom-if was re-attached in new position
-          let lastChild = wrap(this).previousSibling;
-          if (lastChild !== c$[c$.length-1]) {
-            for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) {
-              wrap(parentNode).insertBefore(n, this);
-            }
-          }
-        }
-      }
-    }
-    return true;
-  }
-
-  __syncHostProperties() {
-    let props = this.__invalidProps;
-    if (props) {
-      for (let prop in props) {
-        this.__instance._setPendingProperty(prop, this.__dataHost[prop]);
-      }
-      this.__invalidProps = null;
-      this.__instance._flushProperties();
-    }
-  }
-
-  __teardownInstance() {
-    if (this.__instance) {
-      let c$ = this.__instance.children;
-      if (c$ && c$.length) {
-        // use first child parent, for case when dom-if may have been detached
-        let parent = wrap(c$[0]).parentNode;
-        // Instance children may be disconnected from parents when dom-if
-        // detaches if a tree was innerHTML'ed
-        if (parent) {
-          parent = wrap(parent);
-          for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) {
-            parent.removeChild(n);
-          }
-        }
-      }
-      this.__instance = null;
-      this.__invalidProps = null;
-    }
-  }
-
-  /**
-   * Shows or hides the template instance top level child elements. For
-   * text nodes, `textContent` is removed while "hidden" and replaced when
-   * "shown."
-   * @return {void}
-   * @protected
-   * @suppress {visibility}
-   */
-  _showHideChildren() {
-    let hidden = this.__hideTemplateChildren__ || !this.if;
-    if (this.__instance) {
-      this.__instance._showHideChildren(hidden);
-    }
-  }
-
-}
-
-customElements.define(DomIf.is, DomIf);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-module.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-module.js
deleted file mode 100644
index 02c41e7..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-module.js
+++ /dev/null
@@ -1,163 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-
-import { resolveUrl, pathFromUrl } from '../utils/resolve-url.js';
-import { strictTemplatePolicy } from '../utils/settings.js';
-
-let modules = {};
-let lcModules = {};
-/**
- * Sets a dom-module into the global registry by id.
- *
- * @param {string} id dom-module id
- * @param {DomModule} module dom-module instance
- * @return {void}
- */
-function setModule(id, module) {
-  // store id separate from lowercased id so that
-  // in all cases mixedCase id will stored distinctly
-  // and lowercase version is a fallback
-  modules[id] = lcModules[id.toLowerCase()] = module;
-}
-/**
- * Retrieves a dom-module from the global registry by id.
- *
- * @param {string} id dom-module id
- * @return {DomModule!} dom-module instance
- */
-function findModule(id) {
-  return modules[id] || lcModules[id.toLowerCase()];
-}
-
-function styleOutsideTemplateCheck(inst) {
-  if (inst.querySelector('style')) {
-    console.warn('dom-module %s has style outside template', inst.id);
-  }
-}
-
-/**
- * The `dom-module` element registers the dom it contains to the name given
- * by the module's id attribute. It provides a unified database of dom
- * accessible via its static `import` API.
- *
- * A key use case of `dom-module` is for providing custom element `<template>`s
- * via HTML imports that are parsed by the native HTML parser, that can be
- * relocated during a bundling pass and still looked up by `id`.
- *
- * Example:
- *
- *     <dom-module id="foo">
- *       <img src="stuff.png">
- *     </dom-module>
- *
- * Then in code in some other location that cannot access the dom-module above
- *
- *     let img = customElements.get('dom-module').import('foo', 'img');
- *
- * @customElement
- * @extends HTMLElement
- * @summary Custom element that provides a registry of relocatable DOM content
- *   by `id` that is agnostic to bundling.
- * @unrestricted
- */
-export class DomModule extends HTMLElement {
-
-  static get observedAttributes() { return ['id']; }
-
-  /**
-   * Retrieves the element specified by the css `selector` in the module
-   * registered by `id`. For example, this.import('foo', 'img');
-   * @param {string} id The id of the dom-module in which to search.
-   * @param {string=} selector The css selector by which to find the element.
-   * @return {Element} Returns the element which matches `selector` in the
-   * module registered at the specified `id`.
-   *
-   * @export
-   * @nocollapse Referred to indirectly in style-gather.js
-   */
-  static import(id, selector) {
-    if (id) {
-      let m = findModule(id);
-      if (m && selector) {
-        return m.querySelector(selector);
-      }
-      return m;
-    }
-    return null;
-  }
-
-  /* eslint-disable no-unused-vars */
-  /**
-   * @param {string} name Name of attribute.
-   * @param {?string} old Old value of attribute.
-   * @param {?string} value Current value of attribute.
-   * @param {?string} namespace Attribute namespace.
-   * @return {void}
-   * @override
-   */
-  attributeChangedCallback(name, old, value, namespace) {
-    if (old !== value) {
-      this.register();
-    }
-  }
-  /* eslint-enable no-unused-args */
-
-  /**
-   * The absolute URL of the original location of this `dom-module`.
-   *
-   * This value will differ from this element's `ownerDocument` in the
-   * following ways:
-   * - Takes into account any `assetpath` attribute added during bundling
-   *   to indicate the original location relative to the bundled location
-   * - Uses the HTMLImports polyfill's `importForElement` API to ensure
-   *   the path is relative to the import document's location since
-   *   `ownerDocument` is not currently polyfilled
-   */
-  get assetpath() {
-    // Don't override existing assetpath.
-    if (!this.__assetpath) {
-      // note: assetpath set via an attribute must be relative to this
-      // element's location; accomodate polyfilled HTMLImports
-      const owner = window.HTMLImports && HTMLImports.importForElement ?
-        HTMLImports.importForElement(this) || document : this.ownerDocument;
-      const url = resolveUrl(
-        this.getAttribute('assetpath') || '', owner.baseURI);
-      this.__assetpath = pathFromUrl(url);
-    }
-    return this.__assetpath;
-  }
-
-  /**
-   * Registers the dom-module at a given id. This method should only be called
-   * when a dom-module is imperatively created. For
-   * example, `document.createElement('dom-module').register('foo')`.
-   * @param {string=} id The id at which to register the dom-module.
-   * @return {void}
-   */
-  register(id) {
-    id = id || this.id;
-    if (id) {
-      // Under strictTemplatePolicy, reject and null out any re-registered
-      // dom-module since it is ambiguous whether first-in or last-in is trusted
-      if (strictTemplatePolicy && findModule(id) !== undefined) {
-        setModule(id, null);
-        throw new Error(`strictTemplatePolicy: dom-module ${id} re-registered`);
-      }
-      this.id = id;
-      setModule(id, this);
-      styleOutsideTemplateCheck(this);
-    }
-  }
-}
-
-DomModule.prototype['modules'] = modules;
-
-customElements.define('dom-module', DomModule);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-repeat.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-repeat.js
deleted file mode 100644
index 74f1265..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-repeat.js
+++ /dev/null
@@ -1,733 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import { PolymerElement } from '../../polymer-element.js';
-
-import { TemplateInstanceBase, templatize, modelForElement } from '../utils/templatize.js'; // eslint-disable-line no-unused-vars
-import { Debouncer } from '../utils/debounce.js';
-import { enqueueDebouncer, flush } from '../utils/flush.js';
-import { OptionalMutableData } from '../mixins/mutable-data.js';
-import { matches, translate } from '../utils/path.js';
-import { timeOut, microTask } from '../utils/async.js';
-import { wrap } from '../utils/wrap.js';
-
-/**
- * @constructor
- * @implements {Polymer_OptionalMutableData}
- * @extends {PolymerElement}
- * @private
- */
-const domRepeatBase = OptionalMutableData(PolymerElement);
-
-/**
- * The `<dom-repeat>` element will automatically stamp and binds one instance
- * of template content to each object in a user-provided array.
- * `dom-repeat` accepts an `items` property, and one instance of the template
- * is stamped for each item into the DOM at the location of the `dom-repeat`
- * element.  The `item` property will be set on each instance's binding
- * scope, thus templates should bind to sub-properties of `item`.
- *
- * Example:
- *
- * ```html
- * <dom-module id="employee-list">
- *
- *   <template>
- *
- *     <div> Employee list: </div>
- *     <dom-repeat items="{{employees}}">
- *       <template>
- *         <div>First name: <span>{{item.first}}</span></div>
- *         <div>Last name: <span>{{item.last}}</span></div>
- *       </template>
- *     </dom-repeat>
- *
- *   </template>
- *
- * </dom-module>
- * ```
- *
- * With the following custom element definition:
- *
- * ```js
- * class EmployeeList extends PolymerElement {
- *   static get is() { return 'employee-list'; }
- *   static get properties() {
- *     return {
- *       employees: {
- *         value() {
- *           return [
- *             {first: 'Bob', last: 'Smith'},
- *             {first: 'Sally', last: 'Johnson'},
- *             ...
- *           ];
- *         }
- *       }
- *     };
- *   }
- * }
- * ```
- *
- * Notifications for changes to items sub-properties will be forwarded to template
- * instances, which will update via the normal structured data notification system.
- *
- * Mutations to the `items` array itself should be made using the Array
- * mutation API's on the PropertyEffects mixin (`push`, `pop`, `splice`,
- * `shift`, `unshift`), and template instances will be kept in sync with the
- * data in the array.
- *
- * Events caught by event handlers within the `dom-repeat` template will be
- * decorated with a `model` property, which represents the binding scope for
- * each template instance.  The model should be used to manipulate data on the
- * instance, for example `event.model.set('item.checked', true);`.
- *
- * Alternatively, the model for a template instance for an element stamped by
- * a `dom-repeat` can be obtained using the `modelForElement` API on the
- * `dom-repeat` that stamped it, for example
- * `this.$.domRepeat.modelForElement(event.target).set('item.checked', true);`.
- * This may be useful for manipulating instance data of event targets obtained
- * by event handlers on parents of the `dom-repeat` (event delegation).
- *
- * A view-specific filter/sort may be applied to each `dom-repeat` by supplying a
- * `filter` and/or `sort` property.  This may be a string that names a function on
- * the host, or a function may be assigned to the property directly.  The functions
- * should implemented following the standard `Array` filter/sort API.
- *
- * In order to re-run the filter or sort functions based on changes to sub-fields
- * of `items`, the `observe` property may be set as a space-separated list of
- * `item` sub-fields that should cause a re-filter/sort when modified.  If
- * the filter or sort function depends on properties not contained in `items`,
- * the user should observe changes to those properties and call `render` to update
- * the view based on the dependency change.
- *
- * For example, for an `dom-repeat` with a filter of the following:
- *
- * ```js
- * isEngineer(item) {
- *   return item.type == 'engineer' || item.manager.type == 'engineer';
- * }
- * ```
- *
- * Then the `observe` property should be configured as follows:
- *
- * ```html
- * <dom-repeat items="{{employees}}" filter="isEngineer" observe="type manager.type">
- * ```
- *
- * @customElement
- * @polymer
- * @extends {domRepeatBase}
- * @appliesMixin OptionalMutableData
- * @summary Custom element for stamping instance of a template bound to
- *   items in an array.
- */
-export class DomRepeat extends domRepeatBase {
-
-  // Not needed to find template; can be removed once the analyzer
-  // can find the tag name from customElements.define call
-  static get is() { return 'dom-repeat'; }
-
-  static get template() { return null; }
-
-  static get properties() {
-
-    /**
-     * Fired whenever DOM is added or removed by this template (by
-     * default, rendering occurs lazily).  To force immediate rendering, call
-     * `render`.
-     *
-     * @event dom-change
-     */
-    return {
-
-      /**
-       * An array containing items determining how many instances of the template
-       * to stamp and that that each template instance should bind to.
-       */
-      items: {
-        type: Array
-      },
-
-      /**
-       * The name of the variable to add to the binding scope for the array
-       * element associated with a given template instance.
-       */
-      as: {
-        type: String,
-        value: 'item'
-      },
-
-      /**
-       * The name of the variable to add to the binding scope with the index
-       * of the instance in the sorted and filtered list of rendered items.
-       * Note, for the index in the `this.items` array, use the value of the
-       * `itemsIndexAs` property.
-       */
-      indexAs: {
-        type: String,
-        value: 'index'
-      },
-
-      /**
-       * The name of the variable to add to the binding scope with the index
-       * of the instance in the `this.items` array. Note, for the index of
-       * this instance in the sorted and filtered list of rendered items,
-       * use the value of the `indexAs` property.
-       */
-      itemsIndexAs: {
-        type: String,
-        value: 'itemsIndex'
-      },
-
-      /**
-       * A function that should determine the sort order of the items.  This
-       * property should either be provided as a string, indicating a method
-       * name on the element's host, or else be an actual function.  The
-       * function should match the sort function passed to `Array.sort`.
-       * Using a sort function has no effect on the underlying `items` array.
-       */
-      sort: {
-        type: Function,
-        observer: '__sortChanged'
-      },
-
-      /**
-       * A function that can be used to filter items out of the view.  This
-       * property should either be provided as a string, indicating a method
-       * name on the element's host, or else be an actual function.  The
-       * function should match the sort function passed to `Array.filter`.
-       * Using a filter function has no effect on the underlying `items` array.
-       */
-      filter: {
-        type: Function,
-        observer: '__filterChanged'
-      },
-
-      /**
-       * When using a `filter` or `sort` function, the `observe` property
-       * should be set to a space-separated list of the names of item
-       * sub-fields that should trigger a re-sort or re-filter when changed.
-       * These should generally be fields of `item` that the sort or filter
-       * function depends on.
-       */
-      observe: {
-        type: String,
-        observer: '__observeChanged'
-      },
-
-      /**
-       * When using a `filter` or `sort` function, the `delay` property
-       * determines a debounce time in ms after a change to observed item
-       * properties that must pass before the filter or sort is re-run.
-       * This is useful in rate-limiting shuffling of the view when
-       * item changes may be frequent.
-       */
-      delay: Number,
-
-      /**
-       * Count of currently rendered items after `filter` (if any) has been applied.
-       * If "chunking mode" is enabled, `renderedItemCount` is updated each time a
-       * set of template instances is rendered.
-       *
-       */
-      renderedItemCount: {
-        type: Number,
-        notify: true,
-        readOnly: true
-      },
-
-      /**
-       * Defines an initial count of template instances to render after setting
-       * the `items` array, before the next paint, and puts the `dom-repeat`
-       * into "chunking mode".  The remaining items will be created and rendered
-       * incrementally at each animation frame therof until all instances have
-       * been rendered.
-       */
-      initialCount: {
-        type: Number,
-        observer: '__initializeChunking'
-      },
-
-      /**
-       * When `initialCount` is used, this property defines a frame rate (in
-       * fps) to target by throttling the number of instances rendered each
-       * frame to not exceed the budget for the target frame rate.  The
-       * framerate is effectively the number of `requestAnimationFrame`s that
-       * it tries to allow to actually fire in a given second. It does this
-       * by measuring the time between `rAF`s and continuously adjusting the
-       * number of items created each `rAF` to maintain the target framerate.
-       * Setting this to a higher number allows lower latency and higher
-       * throughput for event handlers and other tasks, but results in a
-       * longer time for the remaining items to complete rendering.
-       */
-      targetFramerate: {
-        type: Number,
-        value: 20
-      },
-
-      _targetFrameTime: {
-        type: Number,
-        computed: '__computeFrameTime(targetFramerate)'
-      }
-
-    };
-
-  }
-
-  static get observers() {
-    return [ '__itemsChanged(items.*)' ];
-  }
-
-  constructor() {
-    super();
-    this.__instances = [];
-    this.__limit = Infinity;
-    this.__pool = [];
-    this.__renderDebouncer = null;
-    this.__itemsIdxToInstIdx = {};
-    this.__chunkCount = null;
-    this.__lastChunkTime = null;
-    this.__sortFn = null;
-    this.__filterFn = null;
-    this.__observePaths = null;
-    /** @type {?function(new:Polymer.TemplateInstanceBase, *)} */
-    this.__ctor = null;
-    this.__isDetached = true;
-    this.template = null;
-  }
-
-  /**
-   * @override
-   * @return {void}
-   */
-  disconnectedCallback() {
-    super.disconnectedCallback();
-    this.__isDetached = true;
-    for (let i=0; i<this.__instances.length; i++) {
-      this.__detachInstance(i);
-    }
-  }
-
-  /**
-   * @override
-   * @return {void}
-   */
-  connectedCallback() {
-    super.connectedCallback();
-    this.style.display = 'none';
-    // only perform attachment if the element was previously detached.
-    if (this.__isDetached) {
-      this.__isDetached = false;
-      let wrappedParent = wrap(wrap(this).parentNode);
-      for (let i=0; i<this.__instances.length; i++) {
-        this.__attachInstance(i, wrappedParent);
-      }
-    }
-  }
-
-  __ensureTemplatized() {
-    // Templatizing (generating the instance constructor) needs to wait
-    // until ready, since won't have its template content handed back to
-    // it until then
-    if (!this.__ctor) {
-      let template = this.template = /** @type {HTMLTemplateElement} */(this.querySelector('template'));
-      if (!template) {
-        // // Wait until childList changes and template should be there by then
-        let observer = new MutationObserver(() => {
-          if (this.querySelector('template')) {
-            observer.disconnect();
-            this.__render();
-          } else {
-            throw new Error('dom-repeat requires a <template> child');
-          }
-        });
-        observer.observe(this, {childList: true});
-        return false;
-      }
-      // Template instance props that should be excluded from forwarding
-      let instanceProps = {};
-      instanceProps[this.as] = true;
-      instanceProps[this.indexAs] = true;
-      instanceProps[this.itemsIndexAs] = true;
-      this.__ctor = templatize(template, this, {
-        mutableData: this.mutableData,
-        parentModel: true,
-        instanceProps: instanceProps,
-        /**
-         * @this {DomRepeat}
-         * @param {string} prop Property to set
-         * @param {*} value Value to set property to
-         */
-        forwardHostProp: function(prop, value) {
-          let i$ = this.__instances;
-          for (let i=0, inst; (i<i$.length) && (inst=i$[i]); i++) {
-            inst.forwardHostProp(prop, value);
-          }
-        },
-        /**
-         * @this {DomRepeat}
-         * @param {Object} inst Instance to notify
-         * @param {string} prop Property to notify
-         * @param {*} value Value to notify
-         */
-        notifyInstanceProp: function(inst, prop, value) {
-          if (matches(this.as, prop)) {
-            let idx = inst[this.itemsIndexAs];
-            if (prop == this.as) {
-              this.items[idx] = value;
-            }
-            let path = translate(this.as, `${JSCompiler_renameProperty('items', this)}.${idx}`, prop);
-            this.notifyPath(path, value);
-          }
-        }
-      });
-    }
-    return true;
-  }
-
-  __getMethodHost() {
-    // Technically this should be the owner of the outermost template.
-    // In shadow dom, this is always getRootNode().host, but we can
-    // approximate this via cooperation with our dataHost always setting
-    // `_methodHost` as long as there were bindings (or id's) on this
-    // instance causing it to get a dataHost.
-    return this.__dataHost._methodHost || this.__dataHost;
-  }
-
-  __functionFromPropertyValue(functionOrMethodName) {
-    if (typeof functionOrMethodName === 'string') {
-      let methodName = functionOrMethodName;
-      let obj = this.__getMethodHost();
-      return function() { return obj[methodName].apply(obj, arguments); };
-    }
-
-    return functionOrMethodName;
-  }
-
-  __sortChanged(sort) {
-    this.__sortFn = this.__functionFromPropertyValue(sort);
-    if (this.items) { this.__debounceRender(this.__render); }
-  }
-
-  __filterChanged(filter) {
-    this.__filterFn = this.__functionFromPropertyValue(filter);
-    if (this.items) { this.__debounceRender(this.__render); }
-  }
-
-  __computeFrameTime(rate) {
-    return Math.ceil(1000/rate);
-  }
-
-  __initializeChunking() {
-    if (this.initialCount) {
-      this.__limit = this.initialCount;
-      this.__chunkCount = this.initialCount;
-      this.__lastChunkTime = performance.now();
-    }
-  }
-
-  __tryRenderChunk() {
-    // Debounced so that multiple calls through `_render` between animation
-    // frames only queue one new rAF (e.g. array mutation & chunked render)
-    if (this.items && this.__limit < this.items.length) {
-      this.__debounceRender(this.__requestRenderChunk);
-    }
-  }
-
-  __requestRenderChunk() {
-    requestAnimationFrame(()=>this.__renderChunk());
-  }
-
-  __renderChunk() {
-    // Simple auto chunkSize throttling algorithm based on feedback loop:
-    // measure actual time between frames and scale chunk count by ratio
-    // of target/actual frame time
-    let currChunkTime = performance.now();
-    let ratio = this._targetFrameTime / (currChunkTime - this.__lastChunkTime);
-    this.__chunkCount = Math.round(this.__chunkCount * ratio) || 1;
-    this.__limit += this.__chunkCount;
-    this.__lastChunkTime = currChunkTime;
-    this.__debounceRender(this.__render);
-  }
-
-  __observeChanged() {
-    this.__observePaths = this.observe &&
-      this.observe.replace('.*', '.').split(' ');
-  }
-
-  __itemsChanged(change) {
-    if (this.items && !Array.isArray(this.items)) {
-      console.warn('dom-repeat expected array for `items`, found', this.items);
-    }
-    // If path was to an item (e.g. 'items.3' or 'items.3.foo'), forward the
-    // path to that instance synchronously (returns false for non-item paths)
-    if (!this.__handleItemPath(change.path, change.value)) {
-      // Otherwise, the array was reset ('items') or spliced ('items.splices'),
-      // so queue a full refresh
-      this.__initializeChunking();
-      this.__debounceRender(this.__render);
-    }
-  }
-
-  __handleObservedPaths(path) {
-    // Handle cases where path changes should cause a re-sort/filter
-    if (this.__sortFn || this.__filterFn) {
-      if (!path) {
-        // Always re-render if the item itself changed
-        this.__debounceRender(this.__render, this.delay);
-      } else if (this.__observePaths) {
-        // Otherwise, re-render if the path changed matches an observed path
-        let paths = this.__observePaths;
-        for (let i=0; i<paths.length; i++) {
-          if (path.indexOf(paths[i]) === 0) {
-            this.__debounceRender(this.__render, this.delay);
-          }
-        }
-      }
-    }
-  }
-
-  /**
-   * @param {function(this:DomRepeat)} fn Function to debounce.
-   * @param {number=} delay Delay in ms to debounce by.
-   */
-  __debounceRender(fn, delay = 0) {
-    this.__renderDebouncer = Debouncer.debounce(
-          this.__renderDebouncer
-        , delay > 0 ? timeOut.after(delay) : microTask
-        , fn.bind(this));
-    enqueueDebouncer(this.__renderDebouncer);
-  }
-
-  /**
-   * Forces the element to render its content. Normally rendering is
-   * asynchronous to a provoking change. This is done for efficiency so
-   * that multiple changes trigger only a single render. The render method
-   * should be called if, for example, template rendering is required to
-   * validate application state.
-   * @return {void}
-   */
-  render() {
-    // Queue this repeater, then flush all in order
-    this.__debounceRender(this.__render);
-    flush();
-  }
-
-  __render() {
-    if (!this.__ensureTemplatized()) {
-      // No template found yet
-      return;
-    }
-    this.__applyFullRefresh();
-    // Reset the pool
-    // TODO(kschaaf): Reuse pool across turns and nested templates
-    // Now that objects/arrays are re-evaluated when set, we can safely
-    // reuse pooled instances across turns, however we still need to decide
-    // semantics regarding how long to hold, how many to hold, etc.
-    this.__pool.length = 0;
-    // Set rendered item count
-    this._setRenderedItemCount(this.__instances.length);
-    // Notify users
-    this.dispatchEvent(new CustomEvent('dom-change', {
-      bubbles: true,
-      composed: true
-    }));
-    // Check to see if we need to render more items
-    this.__tryRenderChunk();
-  }
-
-  __applyFullRefresh() {
-    let items = this.items || [];
-    let isntIdxToItemsIdx = new Array(items.length);
-    for (let i=0; i<items.length; i++) {
-      isntIdxToItemsIdx[i] = i;
-    }
-    // Apply user filter
-    if (this.__filterFn) {
-      isntIdxToItemsIdx = isntIdxToItemsIdx.filter((i, idx, array) =>
-        this.__filterFn(items[i], idx, array));
-    }
-    // Apply user sort
-    if (this.__sortFn) {
-      isntIdxToItemsIdx.sort((a, b) => this.__sortFn(items[a], items[b]));
-    }
-    // items->inst map kept for item path forwarding
-    const itemsIdxToInstIdx = this.__itemsIdxToInstIdx = {};
-    let instIdx = 0;
-    // Generate instances and assign items
-    const limit = Math.min(isntIdxToItemsIdx.length, this.__limit);
-    for (; instIdx<limit; instIdx++) {
-      let inst = this.__instances[instIdx];
-      let itemIdx = isntIdxToItemsIdx[instIdx];
-      let item = items[itemIdx];
-      itemsIdxToInstIdx[itemIdx] = instIdx;
-      if (inst) {
-        inst._setPendingProperty(this.as, item);
-        inst._setPendingProperty(this.indexAs, instIdx);
-        inst._setPendingProperty(this.itemsIndexAs, itemIdx);
-        inst._flushProperties();
-      } else {
-        this.__insertInstance(item, instIdx, itemIdx);
-      }
-    }
-    // Remove any extra instances from previous state
-    for (let i=this.__instances.length-1; i>=instIdx; i--) {
-      this.__detachAndRemoveInstance(i);
-    }
-  }
-
-  __detachInstance(idx) {
-    let inst = this.__instances[idx];
-    const wrappedRoot = wrap(inst.root);
-    for (let i=0; i<inst.children.length; i++) {
-      let el = inst.children[i];
-      wrappedRoot.appendChild(el);
-    }
-    return inst;
-  }
-
-  __attachInstance(idx, parent) {
-    let inst = this.__instances[idx];
-    // Note, this is pre-wrapped as an optimization
-    parent.insertBefore(inst.root, this);
-  }
-
-  __detachAndRemoveInstance(idx) {
-    let inst = this.__detachInstance(idx);
-    if (inst) {
-      this.__pool.push(inst);
-    }
-    this.__instances.splice(idx, 1);
-  }
-
-  __stampInstance(item, instIdx, itemIdx) {
-    let model = {};
-    model[this.as] = item;
-    model[this.indexAs] = instIdx;
-    model[this.itemsIndexAs] = itemIdx;
-    return new this.__ctor(model);
-  }
-
-  __insertInstance(item, instIdx, itemIdx) {
-    let inst = this.__pool.pop();
-    if (inst) {
-      // TODO(kschaaf): If the pool is shared across turns, hostProps
-      // need to be re-set to reused instances in addition to item
-      inst._setPendingProperty(this.as, item);
-      inst._setPendingProperty(this.indexAs, instIdx);
-      inst._setPendingProperty(this.itemsIndexAs, itemIdx);
-      inst._flushProperties();
-    } else {
-      inst = this.__stampInstance(item, instIdx, itemIdx);
-    }
-    let beforeRow = this.__instances[instIdx + 1];
-    let beforeNode = beforeRow ? beforeRow.children[0] : this;
-    wrap(wrap(this).parentNode).insertBefore(inst.root, beforeNode);
-    this.__instances[instIdx] = inst;
-    return inst;
-  }
-
-  // Implements extension point from Templatize mixin
-  /**
-   * Shows or hides the template instance top level child elements. For
-   * text nodes, `textContent` is removed while "hidden" and replaced when
-   * "shown."
-   * @param {boolean} hidden Set to true to hide the children;
-   * set to false to show them.
-   * @return {void}
-   * @protected
-   */
-  _showHideChildren(hidden) {
-    for (let i=0; i<this.__instances.length; i++) {
-      this.__instances[i]._showHideChildren(hidden);
-    }
-  }
-
-  // Called as a side effect of a host items.<key>.<path> path change,
-  // responsible for notifying item.<path> changes to inst for key
-  __handleItemPath(path, value) {
-    let itemsPath = path.slice(6); // 'items.'.length == 6
-    let dot = itemsPath.indexOf('.');
-    let itemsIdx = dot < 0 ? itemsPath : itemsPath.substring(0, dot);
-    // If path was index into array...
-    if (itemsIdx == parseInt(itemsIdx, 10)) {
-      let itemSubPath = dot < 0 ? '' : itemsPath.substring(dot+1);
-      // If the path is observed, it will trigger a full refresh
-      this.__handleObservedPaths(itemSubPath);
-      // Note, even if a rull refresh is triggered, always do the path
-      // notification because unless mutableData is used for dom-repeat
-      // and all elements in the instance subtree, a full refresh may
-      // not trigger the proper update.
-      let instIdx = this.__itemsIdxToInstIdx[itemsIdx];
-      let inst = this.__instances[instIdx];
-      if (inst) {
-        let itemPath = this.as + (itemSubPath ? '.' + itemSubPath : '');
-        // This is effectively `notifyPath`, but avoids some of the overhead
-        // of the public API
-        inst._setPendingPropertyOrPath(itemPath, value, false, true);
-        inst._flushProperties();
-      }
-      return true;
-    }
-  }
-
-  /**
-   * Returns the item associated with a given element stamped by
-   * this `dom-repeat`.
-   *
-   * Note, to modify sub-properties of the item,
-   * `modelForElement(el).set('item.<sub-prop>', value)`
-   * should be used.
-   *
-   * @param {!HTMLElement} el Element for which to return the item.
-   * @return {*} Item associated with the element.
-   */
-  itemForElement(el) {
-    let instance = this.modelForElement(el);
-    return instance && instance[this.as];
-  }
-
-  /**
-   * Returns the inst index for a given element stamped by this `dom-repeat`.
-   * If `sort` is provided, the index will reflect the sorted order (rather
-   * than the original array order).
-   *
-   * @param {!HTMLElement} el Element for which to return the index.
-   * @return {?number} Row index associated with the element (note this may
-   *   not correspond to the array index if a user `sort` is applied).
-   */
-  indexForElement(el) {
-    let instance = this.modelForElement(el);
-    return instance && instance[this.indexAs];
-  }
-
-  /**
-   * Returns the template "model" associated with a given element, which
-   * serves as the binding scope for the template instance the element is
-   * contained in. A template model
-   * should be used to manipulate data associated with this template instance.
-   *
-   * Example:
-   *
-   *   let model = modelForElement(el);
-   *   if (model.index < 10) {
-   *     model.set('item.checked', true);
-   *   }
-   *
-   * @param {!HTMLElement} el Element for which to return a template model.
-   * @return {TemplateInstanceBase} Model representing the binding scope for
-   *   the element.
-   */
-  modelForElement(el) {
-    return modelForElement(this.template, el);
-  }
-
-}
-
-customElements.define(DomRepeat.is, DomRepeat);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/class.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/class.js
deleted file mode 100644
index a35c0c5c..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/class.js
+++ /dev/null
@@ -1,534 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-import { LegacyElementMixin } from './legacy-element-mixin.js';
-import { legacyOptimizations } from '../utils/settings.js';
-
-const lifecycleProps = {
-  attached: true,
-  detached: true,
-  ready: true,
-  created: true,
-  beforeRegister: true,
-  registered: true,
-  attributeChanged: true,
-  listeners: true,
-  hostAttributes: true
-};
-
-const excludeOnInfo = {
-  attached: true,
-  detached: true,
-  ready: true,
-  created: true,
-  beforeRegister: true,
-  registered: true,
-  attributeChanged: true,
-  behaviors: true,
-  _noAccessors: true
-};
-
-const excludeOnBehaviors = Object.assign({
-  listeners: true,
-  hostAttributes: true,
-  properties: true,
-  observers: true,
-}, excludeOnInfo);
-
-function copyProperties(source, target, excludeProps) {
-  const noAccessors = source._noAccessors;
-  const propertyNames = Object.getOwnPropertyNames(source);
-  for (let i = 0; i < propertyNames.length; i++) {
-    let p = propertyNames[i];
-    if (p in excludeProps) {
-      continue;
-    }
-    if (noAccessors) {
-      target[p] = source[p];
-    } else {
-      let pd = Object.getOwnPropertyDescriptor(source, p);
-      if (pd) {
-        // ensure property is configurable so that a later behavior can
-        // re-configure it.
-        pd.configurable = true;
-        Object.defineProperty(target, p, pd);
-      }
-    }
-  }
-}
-
-/**
- * Applies a "legacy" behavior or array of behaviors to the provided class.
- *
- * Note: this method will automatically also apply the `LegacyElementMixin`
- * to ensure that any legacy behaviors can rely on legacy Polymer API on
- * the underlying element.
- *
- * @function
- * @template T
- * @param {!Object|!Array<!Object>} behaviors Behavior object or array of behaviors.
- * @param {function(new:T)} klass Element class.
- * @return {?} Returns a new Element class extended by the
- * passed in `behaviors` and also by `LegacyElementMixin`.
- * @suppress {invalidCasts, checkTypes}
- */
-export function mixinBehaviors(behaviors, klass) {
-  return GenerateClassFromInfo({}, LegacyElementMixin(klass), behaviors);
-}
-
-// NOTE:
-// 1.x
-// Behaviors were mixed in *in reverse order* and de-duped on the fly.
-// The rule was that behavior properties were copied onto the element
-// prototype if and only if the property did not already exist.
-// Given: Polymer{ behaviors: [A, B, C, A, B]}, property copy order was:
-// (1), B, (2), A, (3) C. This means prototype properties win over
-// B properties win over A win over C. This mirrors what would happen
-// with inheritance if element extended B extended A extended C.
-//
-// Again given, Polymer{ behaviors: [A, B, C, A, B]}, the resulting
-// `behaviors` array was [C, A, B].
-// Behavior lifecycle methods were called in behavior array order
-// followed by the element, e.g. (1) C.created, (2) A.created,
-// (3) B.created, (4) element.created. There was no support for
-// super, and "super-behavior" methods were callable only by name).
-//
-// 2.x
-// Behaviors are made into proper mixins which live in the
-// element's prototype chain. Behaviors are placed in the element prototype
-// eldest to youngest and de-duped youngest to oldest:
-// So, first [A, B, C, A, B] becomes [C, A, B] then,
-// the element prototype becomes (oldest) (1) PolymerElement, (2) class(C),
-// (3) class(A), (4) class(B), (5) class(Polymer({...})).
-// Result:
-// This means element properties win over B properties win over A win
-// over C. (same as 1.x)
-// If lifecycle is called (super then me), order is
-// (1) C.created, (2) A.created, (3) B.created, (4) element.created
-// (again same as 1.x)
-function applyBehaviors(proto, behaviors, lifecycle) {
-  for (let i=0; i<behaviors.length; i++) {
-    applyInfo(proto, behaviors[i], lifecycle, excludeOnBehaviors);
-  }
-}
-
-function applyInfo(proto, info, lifecycle, excludeProps) {
-  copyProperties(info, proto, excludeProps);
-  for (let p in lifecycleProps) {
-    if (info[p]) {
-      lifecycle[p] = lifecycle[p] || [];
-      lifecycle[p].push(info[p]);
-    }
-  }
-}
-
-/**
- * @param {Array} behaviors List of behaviors to flatten.
- * @param {Array=} list Target list to flatten behaviors into.
- * @param {Array=} exclude List of behaviors to exclude from the list.
- * @return {!Array} Returns the list of flattened behaviors.
- */
-function flattenBehaviors(behaviors, list, exclude) {
-  list = list || [];
-  for (let i=behaviors.length-1; i >= 0; i--) {
-    let b = behaviors[i];
-    if (b) {
-      if (Array.isArray(b)) {
-        flattenBehaviors(b, list);
-      } else {
-        // dedup
-        if (list.indexOf(b) < 0 && (!exclude || exclude.indexOf(b) < 0)) {
-          list.unshift(b);
-        }
-      }
-    } else {
-      console.warn('behavior is null, check for missing or 404 import');
-    }
-  }
-  return list;
-}
-
-/**
- * Copies property descriptors from source to target, overwriting all fields
- * of any previous descriptor for a property *except* for `value`, which is
- * merged in from the target if it does not exist on the source.
- * 
- * @param {*} target Target properties object
- * @param {*} source Source properties object
- */
-function mergeProperties(target, source) {
-  for (const p in source) {
-    const targetInfo = target[p];
-    const sourceInfo = source[p];
-    if (!('value' in sourceInfo) && targetInfo && ('value' in targetInfo)) {
-      target[p] = Object.assign({value: targetInfo.value}, sourceInfo);
-    } else {
-      target[p] = sourceInfo;
-    }
-  }
-}
-
-/* Note about construction and extension of legacy classes.
-  [Changed in Q4 2018 to optimize performance.]
-
-  When calling `Polymer` or `mixinBehaviors`, the generated class below is
-  made. The list of behaviors was previously made into one generated class per
-  behavior, but this is no longer the case as behaviors are now called
-  manually. Note, there may *still* be multiple generated classes in the
-  element's prototype chain if extension is used with `mixinBehaviors`.
-
-  The generated class is directly tied to the info object and behaviors
-  used to create it. That list of behaviors is filtered so it's only the
-  behaviors not active on the superclass. In order to call through to the
-  entire list of lifecycle methods, it's important to call `super`.
-
-  The element's `properties` and `observers` are controlled via the finalization
-  mechanism provided by `PropertiesMixin`. `Properties` and `observers` are
-  collected by manually traversing the prototype chain and merging.
-
-  To limit changes, the `_registered` method is called via `_initializeProperties`
-  and not `_finalizeClass`.
-
-*/
-/**
- * @param {!PolymerInit} info Polymer info object
- * @param {function(new:HTMLElement)} Base base class to extend with info object
- * @param {Object=} behaviors behaviors to copy into the element
- * @return {function(new:HTMLElement)} Generated class
- * @suppress {checkTypes}
- * @private
- */
-function GenerateClassFromInfo(info, Base, behaviors) {
-
-  // manages behavior and lifecycle processing (filled in after class definition)
-  let behaviorList;
-  const lifecycle = {};
-
-  /** @private */
-  class PolymerGenerated extends Base {
-
-    // explicitly not calling super._finalizeClass
-    static _finalizeClass() {
-      // if calling via a subclass that hasn't been generated, pass through to super
-      if (!this.hasOwnProperty(JSCompiler_renameProperty('generatedFrom', this))) {
-        super._finalizeClass();
-      } else {
-        // interleave properties and observers per behavior and `info`
-        if (behaviorList) {
-          for (let i=0, b; i < behaviorList.length; i++) {
-            b = behaviorList[i];
-            if (b.properties) {
-              this.createProperties(b.properties);
-            }
-            if (b.observers) {
-              this.createObservers(b.observers, b.properties);
-            }
-          }
-        }
-        if (info.properties) {
-          this.createProperties(info.properties);
-        }
-        if (info.observers) {
-          this.createObservers(info.observers, info.properties);
-        }
-        // make sure to prepare the element template
-        this._prepareTemplate();
-      }
-    }
-
-    static get properties() {
-      const properties = {};
-      if (behaviorList) {
-        for (let i=0; i < behaviorList.length; i++) {
-          mergeProperties(properties, behaviorList[i].properties);
-        }
-      }
-      mergeProperties(properties, info.properties);
-      return properties;
-    }
-
-    static get observers() {
-      let observers = [];
-      if (behaviorList) {
-        for (let i=0, b; i < behaviorList.length; i++) {
-          b = behaviorList[i];
-          if (b.observers) {
-            observers = observers.concat(b.observers);
-          }
-        }
-      }
-      if (info.observers) {
-        observers = observers.concat(info.observers);
-      }
-      return observers;
-    }
-
-    /**
-     * @return {void}
-     */
-    created() {
-      super.created();
-      const list = lifecycle.created;
-      if (list) {
-        for (let i=0; i < list.length; i++) {
-          list[i].call(this);
-        }
-      }
-    }
-
-    /**
-     * @return {void}
-     */
-    _registered() {
-      /* NOTE: `beforeRegister` is called here for bc, but the behavior
-        is different than in 1.x. In 1.0, the method was called *after*
-        mixing prototypes together but *before* processing of meta-objects.
-        However, dynamic effects can still be set here and can be done either
-        in `beforeRegister` or `registered`. It is no longer possible to set
-        `is` in `beforeRegister` as you could in 1.x.
-      */
-      // only proceed if the generated class' prototype has not been registered.
-      const generatedProto = PolymerGenerated.prototype;
-      if (!generatedProto.hasOwnProperty('__hasRegisterFinished')) {
-        generatedProto.__hasRegisterFinished = true;
-        // ensure superclass is registered first.
-        super._registered();
-        // copy properties onto the generated class lazily if we're optimizing,
-        if (legacyOptimizations) {
-          copyPropertiesToProto(generatedProto);
-        }
-        // make sure legacy lifecycle is called on the *element*'s prototype
-        // and not the generated class prototype; if the element has been
-        // extended, these are *not* the same.
-        const proto = Object.getPrototypeOf(this);
-        let list = lifecycle.beforeRegister;
-        if (list) {
-          for (let i=0; i < list.length; i++) {
-            list[i].call(proto);
-          }
-        }
-        list = lifecycle.registered;
-        if (list) {
-          for (let i=0; i < list.length; i++) {
-            list[i].call(proto);
-          }
-        }
-      }
-    }
-
-    /**
-     * @return {void}
-     */
-    _applyListeners() {
-      super._applyListeners();
-      const list = lifecycle.listeners;
-      if (list) {
-        for (let i=0; i < list.length; i++) {
-          const listeners = list[i];
-          if (listeners) {
-            for (let l in listeners) {
-              this._addMethodEventListenerToNode(this, l, listeners[l]);
-            }
-          }
-        }
-      }
-    }
-
-    // note: exception to "super then me" rule;
-    // do work before calling super so that super attributes
-    // only apply if not already set.
-    /**
-     * @return {void}
-     */
-    _ensureAttributes() {
-      const list = lifecycle.hostAttributes;
-      if (list) {
-        for (let i=list.length-1; i >= 0; i--) {
-          const hostAttributes = list[i];
-          for (let a in hostAttributes) {
-              this._ensureAttribute(a, hostAttributes[a]);
-            }
-        }
-      }
-      super._ensureAttributes();
-    }
-
-    /**
-     * @return {void}
-     */
-    ready() {
-      super.ready();
-      let list = lifecycle.ready;
-      if (list) {
-        for (let i=0; i < list.length; i++) {
-          list[i].call(this);
-        }
-      }
-    }
-
-    /**
-     * @return {void}
-     */
-    attached() {
-      super.attached();
-      let list = lifecycle.attached;
-      if (list) {
-        for (let i=0; i < list.length; i++) {
-          list[i].call(this);
-        }
-      }
-    }
-
-    /**
-     * @return {void}
-     */
-    detached() {
-      super.detached();
-      let list = lifecycle.detached;
-      if (list) {
-        for (let i=0; i < list.length; i++) {
-          list[i].call(this);
-        }
-      }
-    }
-
-    /**
-     * Implements native Custom Elements `attributeChangedCallback` to
-     * set an attribute value to a property via `_attributeToProperty`.
-     *
-     * @param {string} name Name of attribute that changed
-     * @param {?string} old Old attribute value
-     * @param {?string} value New attribute value
-     * @return {void}
-     */
-    attributeChanged(name, old, value) {
-      super.attributeChanged();
-      let list = lifecycle.attributeChanged;
-      if (list) {
-        for (let i=0; i < list.length; i++) {
-          list[i].call(this, name, old, value);
-        }
-      }
-    }
-  }
-
-  // apply behaviors, note actual copying is done lazily at first instance creation
-  if (behaviors) {
-    // NOTE: ensure the behavior is extending a class with
-    // legacy element api. This is necessary since behaviors expect to be able
-    // to access 1.x legacy api.
-    if (!Array.isArray(behaviors)) {
-      behaviors = [behaviors];
-    }
-    let superBehaviors = Base.prototype.behaviors;
-    // get flattened, deduped list of behaviors *not* already on super class
-    behaviorList = flattenBehaviors(behaviors, null, superBehaviors);
-    PolymerGenerated.prototype.behaviors = superBehaviors ?
-      superBehaviors.concat(behaviors) : behaviorList;
-  }
-
-  const copyPropertiesToProto = (proto) => {
-    if (behaviorList) {
-      applyBehaviors(proto, behaviorList, lifecycle);
-    }
-    applyInfo(proto, info, lifecycle, excludeOnInfo);
-  };
-
-  // copy properties if we're not optimizing
-  if (!legacyOptimizations) {
-    copyPropertiesToProto(PolymerGenerated.prototype);
-  }
-
-  PolymerGenerated.generatedFrom = info;
-
-  return PolymerGenerated;
-}
-
-/**
- * Generates a class that extends `LegacyElement` based on the
- * provided info object.  Metadata objects on the `info` object
- * (`properties`, `observers`, `listeners`, `behaviors`, `is`) are used
- * for Polymer's meta-programming systems, and any functions are copied
- * to the generated class.
- *
- * Valid "metadata" values are as follows:
- *
- * `is`: String providing the tag name to register the element under. In
- * addition, if a `dom-module` with the same id exists, the first template
- * in that `dom-module` will be stamped into the shadow root of this element,
- * with support for declarative event listeners (`on-...`), Polymer data
- * bindings (`[[...]]` and `{{...}}`), and id-based node finding into
- * `this.$`.
- *
- * `properties`: Object describing property-related metadata used by Polymer
- * features (key: property names, value: object containing property metadata).
- * Valid keys in per-property metadata include:
- * - `type` (String|Number|Object|Array|...): Used by
- *   `attributeChangedCallback` to determine how string-based attributes
- *   are deserialized to JavaScript property values.
- * - `notify` (boolean): Causes a change in the property to fire a
- *   non-bubbling event called `<property>-changed`. Elements that have
- *   enabled two-way binding to the property use this event to observe changes.
- * - `readOnly` (boolean): Creates a getter for the property, but no setter.
- *   To set a read-only property, use the private setter method
- *   `_setProperty(property, value)`.
- * - `observer` (string): Observer method name that will be called when
- *   the property changes. The arguments of the method are
- *   `(value, previousValue)`.
- * - `computed` (string): String describing method and dependent properties
- *   for computing the value of this property (e.g. `'computeFoo(bar, zot)'`).
- *   Computed properties are read-only by default and can only be changed
- *   via the return value of the computing method.
- *
- * `observers`: Array of strings describing multi-property observer methods
- *  and their dependent properties (e.g. `'observeABC(a, b, c)'`).
- *
- * `listeners`: Object describing event listeners to be added to each
- *  instance of this element (key: event name, value: method name).
- *
- * `behaviors`: Array of additional `info` objects containing metadata
- * and callbacks in the same format as the `info` object here which are
- * merged into this element.
- *
- * `hostAttributes`: Object listing attributes to be applied to the host
- *  once created (key: attribute name, value: attribute value).  Values
- *  are serialized based on the type of the value.  Host attributes should
- *  generally be limited to attributes such as `tabIndex` and `aria-...`.
- *  Attributes in `hostAttributes` are only applied if a user-supplied
- *  attribute is not already present (attributes in markup override
- *  `hostAttributes`).
- *
- * In addition, the following Polymer-specific callbacks may be provided:
- * - `registered`: called after first instance of this element,
- * - `created`: called during `constructor`
- * - `attached`: called during `connectedCallback`
- * - `detached`: called during `disconnectedCallback`
- * - `ready`: called before first `attached`, after all properties of
- *   this element have been propagated to its template and all observers
- *   have run
- *
- * @param {!PolymerInit} info Object containing Polymer metadata and functions
- *   to become class methods.
- * @template T
- * @param {function(T):T} mixin Optional mixin to apply to legacy base class
- *   before extending with Polymer metaprogramming.
- * @return {function(new:HTMLElement)} Generated class
- */
-export const Class = function(info, mixin) {
-  if (!info) {
-    console.warn('Polymer.Class requires `info` argument');
-  }
-  let klass = mixin ? mixin(LegacyElementMixin(HTMLElement)) :
-      LegacyElementMixin(HTMLElement);
-  klass = GenerateClassFromInfo(info, klass, info.behaviors);
-  // decorate klass with registration info
-  klass.is = klass.prototype.is = info.is;
-  return klass;
-};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-data-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-data-mixin.js
deleted file mode 100644
index b2a2553..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-data-mixin.js
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-import { Class } from './class.js';
-import { Polymer } from '../../polymer-legacy.js';
-import { dedupingMixin } from '../utils/mixin.js';
-import { templatize } from '../utils/templatize.js';
-
-const UndefinedArgumentError = class extends Error {
-  constructor(message, arg) {
-    super(message);
-    this.arg = arg;
-    this.name = this.constructor.name;
-    // Affordances for ensuring instanceof works after babel ES5 compilation
-    // TODO(kschaaf): Remove after polymer CLI updates to newer Babel that
-    // sets the constructor/prototype correctly for subclassed builtins
-    this.constructor = UndefinedArgumentError;
-    this.__proto__ = UndefinedArgumentError.prototype;
-  }
-};
-
-/**
- * Wraps effect functions to catch `UndefinedArgumentError`s and warn.
- *
- * @param {Object=} effect Effect metadata object
- * @param {Object=} fnName Name of user function, if known
- * @return {?Object|undefined} Effect metadata object
- */
-function wrapEffect(effect, fnName) {
-  if (effect && effect.fn) {
-    const fn = effect.fn;
-    effect.fn = function() {
-      try {
-        fn.apply(this, arguments);
-      } catch (e) {
-        if (e instanceof UndefinedArgumentError) {
-          console.warn(`Argument '${e.arg}'${fnName ?` for method '${fnName}'` : ''} was undefined. Ensure it has a default value, or else ensure the method handles the argument being undefined.`);
-        } else {
-          throw e;
-        }
-      }
-    };
-  }
-  return effect;
-}
-
-/**
- * Mixin to selectively add back Polymer 1.x's `undefined` rules
- * governing when observers & computing functions run based
- * on all arguments being defined (reference https://www.polymer-project.org/1.0/docs/devguide/observers#multi-property-observers).
- *
- * When loaded, all legacy elements (defined with `Polymer({...})`)
- * will have the mixin applied. The mixin only restores legacy data handling
- * if `_legacyUndefinedCheck: true` is set on the element's prototype.
- *
- * This mixin is intended for use to help migration from Polymer 1.x to
- * 2.x+ by allowing legacy code to work while identifying observers and
- * computing functions that need undefined checks to work without
- * the mixin in Polymer 2.
- *
- * @mixinFunction
- * @polymer
- * @summary Mixin to selectively add back Polymer 1.x's `undefined` rules
- * governing when observers & computing functions run.
- */
-export const LegacyDataMixin = dedupingMixin(superClass => {
-
-  /**
-   * @unrestricted
-   * @private
-   */
-  class LegacyDataMixin extends superClass {
-    /**
-     * Overrides `Polymer.PropertyEffects` to add `undefined` argument
-     * checking to match Polymer 1.x style rules
-     *
-     * @param {!Array<!MethodArg>} args Array of argument metadata
-     * @param {string} path Property/path name that triggered the method effect
-     * @param {Object} props Bag of current property changes
-     * @return {Array<*>} Array of argument values
-     * @private
-     */
-    _marshalArgs(args, path, props) {
-      const vals = super._marshalArgs(args, path, props);
-      // Per legacy data rules, single-property observers (whether in `properties`
-      // and in `observers`) are called regardless of whether their argument is
-      // undefined or not. Multi-property observers must have all arguments defined
-      if (this._legacyUndefinedCheck && vals.length > 1) {
-        for (let i=0; i<vals.length; i++) {
-          if (vals[i] === undefined) {
-            // Break out of effect's control flow; will be caught in
-            // wrapped property effect function below
-            const name = args[i].name;
-            throw new UndefinedArgumentError(`Argument '${name}' is undefined.`, name);
-          }
-        }
-      }
-      return vals;
-    }
-
-    /**
-     * Overrides `Polyer.PropertyEffects` to wrap effect functions to
-     * catch `UndefinedArgumentError`s and warn.
-     *
-     * @param {string} property Property that should trigger the effect
-     * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
-     * @param {Object=} effect Effect metadata object
-     * @return {void}
-     * @protected
-     */
-    _addPropertyEffect(property, type, effect) {
-      return super._addPropertyEffect(property, type,
-        wrapEffect(effect, effect && effect.info && effect.info.methodName));
-    }
-
-    /**
-     * Overrides `Polyer.PropertyEffects` to wrap effect functions to
-     * catch `UndefinedArgumentError`s and warn.
-     *
-     * @param {Object} templateInfo Template metadata to add effect to
-     * @param {string} prop Property that should trigger the effect
-     * @param {Object=} effect Effect metadata object
-     * @return {void}
-     * @protected
-     */
-    static _addTemplatePropertyEffect(templateInfo, prop, effect) {
-      return super._addTemplatePropertyEffect(templateInfo, prop, wrapEffect(effect));
-    }
-
-  }
-
-  return LegacyDataMixin;
-
-});
-
-// LegacyDataMixin is applied to base class _before_ metaprogramming, to
-// ensure override of _addPropertyEffect et.al. are used by metaprogramming
-// performed in _finalizeClass
-Polymer.Class = (info, mixin) => Class(info,
-  superClass => mixin ?
-    mixin(LegacyDataMixin(superClass)) :
-    LegacyDataMixin(superClass)
-);
-
-// Apply LegacyDataMixin to Templatizer instances as well, and defer
-// runtime switch to the root's host (_methodHost)
-/**
- * @mixinFunction
- * @polymer
- */
-const TemplatizeMixin =
-  dedupingMixin(superClass => {
-    /**
-     * @constructor
-     * @extends {HTMLElement}
-     */
-    const legacyBase = LegacyDataMixin(superClass);
-    /**
-     * @private
-     */
-    class TemplateLegacy extends legacyBase {
-      get _legacyUndefinedCheck() {
-        return this._methodHost && this._methodHost._legacyUndefinedCheck;
-      }
-    }
-    /** @type {!Polymer_PropertyEffects} */
-    TemplateLegacy.prototype._methodHost;
-    return TemplateLegacy;
-  });
-
-templatize.mixin = TemplatizeMixin;
-
-console.info('LegacyDataMixin will be applied to all legacy elements.\n' +
-              'Set `_legacyUndefinedCheck: true` on element class to enable.');
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-element-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-element-mixin.js
deleted file mode 100644
index bd591b6d..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-element-mixin.js
+++ /dev/null
@@ -1,1059 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../../../shadycss/entrypoints/apply-shim.js';
-import { ElementMixin } from '../mixins/element-mixin.js';
-import { GestureEventListeners } from '../mixins/gesture-event-listeners.js';
-import { dedupingMixin } from '../utils/mixin.js';
-import '../utils/render-status.js';
-import '../utils/unresolved.js';
-import { dom, matchesSelector } from './polymer.dom.js';
-import { setTouchAction } from '../utils/gestures.js';
-import { Debouncer } from '../utils/debounce.js';
-import { timeOut, microTask } from '../utils/async.js';
-import { get } from '../utils/path.js';
-import { wrap } from '../utils/wrap.js';
-
-let styleInterface = window.ShadyCSS;
-
-/**
- * Element class mixin that provides Polymer's "legacy" API intended to be
- * backward-compatible to the greatest extent possible with the API
- * found on the Polymer 1.x `Polymer.Base` prototype applied to all elements
- * defined using the `Polymer({...})` function.
- *
- * @mixinFunction
- * @polymer
- * @appliesMixin ElementMixin
- * @appliesMixin GestureEventListeners
- * @property isAttached {boolean} Set to `true` in this element's
- *   `connectedCallback` and `false` in `disconnectedCallback`
- * @summary Element class mixin that provides Polymer's "legacy" API
- */
-export const LegacyElementMixin = dedupingMixin((base) => {
-  /**
-   * @constructor
-   * @implements {Polymer_ElementMixin}
-   * @implements {Polymer_GestureEventListeners}
-   * @extends {HTMLElement}
-   * @private
-   */
-  const legacyElementBase = GestureEventListeners(ElementMixin(base));
-
-  /**
-   * Map of simple names to touch action names
-   * @dict
-   */
-  const DIRECTION_MAP = {
-    'x': 'pan-x',
-    'y': 'pan-y',
-    'none': 'none',
-    'all': 'auto'
-  };
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @extends {legacyElementBase}
-   * @implements {Polymer_LegacyElementMixin}
-   * @unrestricted
-   */
-  class LegacyElement extends legacyElementBase {
-
-    constructor() {
-      super();
-      /** @type {boolean} */
-      this.isAttached;
-      /** @type {?WeakMap<!Element, !Object<string, !Function>>} */
-      this.__boundListeners;
-      /** @type {?Object<string, ?Function>} */
-      this._debouncers;
-    }
-
-    /**
-     * Forwards `importMeta` from the prototype (i.e. from the info object
-     * passed to `Polymer({...})`) to the static API.
-     *
-     * @return {!Object} The `import.meta` object set on the prototype
-     * @suppress {missingProperties} `this` is always in the instance in
-     *  closure for some reason even in a static method, rather than the class
-     */
-    static get importMeta() {
-      return this.prototype.importMeta;
-    }
-
-    /**
-     * Legacy callback called during the `constructor`, for overriding
-     * by the user.
-     * @override
-     * @return {void}
-     */
-    created() {}
-
-    /**
-     * Provides an implementation of `connectedCallback`
-     * which adds Polymer legacy API's `attached` method.
-     * @return {void}
-     * @override
-     */
-    connectedCallback() {
-      super.connectedCallback();
-      this.isAttached = true;
-      this.attached();
-    }
-
-    /**
-     * Legacy callback called during `connectedCallback`, for overriding
-     * by the user.
-     * @override
-     * @return {void}
-     */
-    attached() {}
-
-    /**
-     * Provides an implementation of `disconnectedCallback`
-     * which adds Polymer legacy API's `detached` method.
-     * @return {void}
-     * @override
-     */
-    disconnectedCallback() {
-      super.disconnectedCallback();
-      this.isAttached = false;
-      this.detached();
-    }
-
-    /**
-     * Legacy callback called during `disconnectedCallback`, for overriding
-     * by the user.
-     * @override
-     * @return {void}
-     */
-    detached() {}
-
-    /**
-     * Provides an override implementation of `attributeChangedCallback`
-     * which adds the Polymer legacy API's `attributeChanged` method.
-     * @param {string} name Name of attribute.
-     * @param {?string} old Old value of attribute.
-     * @param {?string} value Current value of attribute.
-     * @param {?string} namespace Attribute namespace.
-     * @return {void}
-     * @override
-     */
-    attributeChangedCallback(name, old, value, namespace) {
-      if (old !== value) {
-        super.attributeChangedCallback(name, old, value, namespace);
-        this.attributeChanged(name, old, value);
-      }
-    }
-
-    /**
-     * Legacy callback called during `attributeChangedChallback`, for overriding
-     * by the user.
-     * @param {string} name Name of attribute.
-     * @param {?string} old Old value of attribute.
-     * @param {?string} value Current value of attribute.
-     * @return {void}
-     * @override
-     */
-    attributeChanged(name, old, value) {} // eslint-disable-line no-unused-vars
-
-    /**
-     * Overrides the default `Polymer.PropertyEffects` implementation to
-     * add support for class initialization via the `_registered` callback.
-     * This is called only when the first instance of the element is created.
-     *
-     * @return {void}
-     * @override
-     * @suppress {invalidCasts}
-     */
-    _initializeProperties() {
-      let proto = Object.getPrototypeOf(this);
-      if (!proto.hasOwnProperty('__hasRegisterFinished')) {
-        this._registered();
-        // backstop in case the `_registered` implementation does not set this
-        proto.__hasRegisterFinished = true;
-      }
-      super._initializeProperties();
-      this.root = /** @type {HTMLElement} */(this);
-      this.created();
-      // Ensure listeners are applied immediately so that they are
-      // added before declarative event listeners. This allows an element to
-      // decorate itself via an event prior to any declarative listeners
-      // seeing the event. Note, this ensures compatibility with 1.x ordering.
-      this._applyListeners();
-    }
-
-    /**
-     * Called automatically when an element is initializing.
-     * Users may override this method to perform class registration time
-     * work. The implementation should ensure the work is performed
-     * only once for the class.
-     * @protected
-     * @return {void}
-     * @override
-     */
-    _registered() {}
-
-    /**
-     * Overrides the default `Polymer.PropertyEffects` implementation to
-     * add support for installing `hostAttributes` and `listeners`.
-     *
-     * @return {void}
-     * @override
-     */
-    ready() {
-      this._ensureAttributes();
-      super.ready();
-    }
-
-    /**
-     * Ensures an element has required attributes. Called when the element
-     * is being readied via `ready`. Users should override to set the
-     * element's required attributes. The implementation should be sure
-     * to check and not override existing attributes added by
-     * the user of the element. Typically, setting attributes should be left
-     * to the element user and not done here; reasonable exceptions include
-     * setting aria roles and focusability.
-     * @protected
-     * @return {void}
-     * @override
-     */
-    _ensureAttributes() {}
-
-    /**
-     * Adds element event listeners. Called when the element
-     * is being readied via `ready`. Users should override to
-     * add any required element event listeners.
-     * In performance critical elements, the work done here should be kept
-     * to a minimum since it is done before the element is rendered. In
-     * these elements, consider adding listeners asynchronously so as not to
-     * block render.
-     * @protected
-     * @return {void}
-     * @override
-     */
-    _applyListeners() {}
-
-    /**
-     * Converts a typed JavaScript value to a string.
-     *
-     * Note this method is provided as backward-compatible legacy API
-     * only.  It is not directly called by any Polymer features. To customize
-     * how properties are serialized to attributes for attribute bindings and
-     * `reflectToAttribute: true` properties as well as this method, override
-     * the `_serializeValue` method provided by `Polymer.PropertyAccessors`.
-     *
-     * @param {*} value Value to deserialize
-     * @return {string | undefined} Serialized value
-     * @override
-     */
-    serialize(value) {
-      return this._serializeValue(value);
-    }
-
-    /**
-     * Converts a string to a typed JavaScript value.
-     *
-     * Note this method is provided as backward-compatible legacy API
-     * only.  It is not directly called by any Polymer features.  To customize
-     * how attributes are deserialized to properties for in
-     * `attributeChangedCallback`, override `_deserializeValue` method
-     * provided by `Polymer.PropertyAccessors`.
-     *
-     * @param {string} value String to deserialize
-     * @param {*} type Type to deserialize the string to
-     * @return {*} Returns the deserialized value in the `type` given.
-     * @override
-     */
-    deserialize(value, type) {
-      return this._deserializeValue(value, type);
-    }
-
-    /**
-     * Serializes a property to its associated attribute.
-     *
-     * Note this method is provided as backward-compatible legacy API
-     * only.  It is not directly called by any Polymer features.
-     *
-     * @param {string} property Property name to reflect.
-     * @param {string=} attribute Attribute name to reflect.
-     * @param {*=} value Property value to reflect.
-     * @return {void}
-     * @override
-     */
-    reflectPropertyToAttribute(property, attribute, value) {
-      this._propertyToAttribute(property, attribute, value);
-    }
-
-    /**
-     * Sets a typed value to an HTML attribute on a node.
-     *
-     * Note this method is provided as backward-compatible legacy API
-     * only.  It is not directly called by any Polymer features.
-     *
-     * @param {*} value Value to serialize.
-     * @param {string} attribute Attribute name to serialize to.
-     * @param {Element} node Element to set attribute to.
-     * @return {void}
-     * @override
-     */
-    serializeValueToAttribute(value, attribute, node) {
-      this._valueToNodeAttribute(/** @type {Element} */ (node || this), value, attribute);
-    }
-
-    /**
-     * Copies own properties (including accessor descriptors) from a source
-     * object to a target object.
-     *
-     * @param {Object} prototype Target object to copy properties to.
-     * @param {Object} api Source object to copy properties from.
-     * @return {Object} prototype object that was passed as first argument.
-     * @override
-     */
-    extend(prototype, api) {
-      if (!(prototype && api)) {
-        return prototype || api;
-      }
-      let n$ = Object.getOwnPropertyNames(api);
-      for (let i=0, n; (i<n$.length) && (n=n$[i]); i++) {
-        let pd = Object.getOwnPropertyDescriptor(api, n);
-        if (pd) {
-          Object.defineProperty(prototype, n, pd);
-        }
-      }
-      return prototype;
-    }
-
-    /**
-     * Copies props from a source object to a target object.
-     *
-     * Note, this method uses a simple `for...in` strategy for enumerating
-     * properties.  To ensure only `ownProperties` are copied from source
-     * to target and that accessor implementations are copied, use `extend`.
-     *
-     * @param {!Object} target Target object to copy properties to.
-     * @param {!Object} source Source object to copy properties from.
-     * @return {!Object} Target object that was passed as first argument.
-     * @override
-     */
-    mixin(target, source) {
-      for (let i in source) {
-        target[i] = source[i];
-      }
-      return target;
-    }
-
-    /**
-     * Sets the prototype of an object.
-     *
-     * Note this method is provided as backward-compatible legacy API
-     * only.  It is not directly called by any Polymer features.
-     * @param {Object} object The object on which to set the prototype.
-     * @param {Object} prototype The prototype that will be set on the given
-     * `object`.
-     * @return {Object} Returns the given `object` with its prototype set
-     * to the given `prototype` object.
-     * @override
-     */
-    chainObject(object, prototype) {
-      if (object && prototype && object !== prototype) {
-        object.__proto__ = prototype;
-      }
-      return object;
-    }
-
-    /* **** Begin Template **** */
-
-    /**
-     * Calls `importNode` on the `content` of the `template` specified and
-     * returns a document fragment containing the imported content.
-     *
-     * @param {HTMLTemplateElement} template HTML template element to instance.
-     * @return {!DocumentFragment} Document fragment containing the imported
-     *   template content.
-     * @override
-     * @suppress {missingProperties} go/missingfnprops
-     */
-    instanceTemplate(template) {
-      let content = this.constructor._contentForTemplate(template);
-      let dom = /** @type {!DocumentFragment} */
-        (document.importNode(content, true));
-      return dom;
-    }
-
-    /* **** Begin Events **** */
-
-
-
-    /**
-     * Dispatches a custom event with an optional detail value.
-     *
-     * @param {string} type Name of event type.
-     * @param {*=} detail Detail value containing event-specific
-     *   payload.
-     * @param {{ bubbles: (boolean|undefined), cancelable: (boolean|undefined),
-     *     composed: (boolean|undefined) }=}
-     *  options Object specifying options.  These may include:
-     *  `bubbles` (boolean, defaults to `true`),
-     *  `cancelable` (boolean, defaults to false), and
-     *  `node` on which to fire the event (HTMLElement, defaults to `this`).
-     * @return {!Event} The new event that was fired.
-     * @override
-     */
-    fire(type, detail, options) {
-      options = options || {};
-      detail = (detail === null || detail === undefined) ? {} : detail;
-      let event = new Event(type, {
-        bubbles: options.bubbles === undefined ? true : options.bubbles,
-        cancelable: Boolean(options.cancelable),
-        composed: options.composed === undefined ? true: options.composed
-      });
-      event.detail = detail;
-      let node = options.node || this;
-      wrap(node).dispatchEvent(event);
-      return event;
-    }
-
-    /**
-     * Convenience method to add an event listener on a given element,
-     * late bound to a named method on this element.
-     *
-     * @param {?EventTarget} node Element to add event listener to.
-     * @param {string} eventName Name of event to listen for.
-     * @param {string} methodName Name of handler method on `this` to call.
-     * @return {void}
-     * @override
-     */
-    listen(node, eventName, methodName) {
-      node = /** @type {!EventTarget} */ (node || this);
-      let hbl = this.__boundListeners ||
-        (this.__boundListeners = new WeakMap());
-      let bl = hbl.get(node);
-      if (!bl) {
-        bl = {};
-        hbl.set(node, bl);
-      }
-      let key = eventName + methodName;
-      if (!bl[key]) {
-        bl[key] = this._addMethodEventListenerToNode(
-            /** @type {!Node} */ (node), eventName, methodName, this);
-      }
-    }
-
-    /**
-     * Convenience method to remove an event listener from a given element,
-     * late bound to a named method on this element.
-     *
-     * @param {?EventTarget} node Element to remove event listener from.
-     * @param {string} eventName Name of event to stop listening to.
-     * @param {string} methodName Name of handler method on `this` to not call
-     anymore.
-     * @return {void}
-     * @override
-     */
-    unlisten(node, eventName, methodName) {
-      node = /** @type {!EventTarget} */ (node || this);
-      let bl = this.__boundListeners &&
-          this.__boundListeners.get(/** @type {!Element} */ (node));
-      let key = eventName + methodName;
-      let handler = bl && bl[key];
-      if (handler) {
-        this._removeEventListenerFromNode(
-            /** @type {!Node} */ (node), eventName, handler);
-        bl[key] = /** @type {?} */ (null);
-      }
-    }
-
-    /**
-     * Override scrolling behavior to all direction, one direction, or none.
-     *
-     * Valid scroll directions:
-     *   - 'all': scroll in any direction
-     *   - 'x': scroll only in the 'x' direction
-     *   - 'y': scroll only in the 'y' direction
-     *   - 'none': disable scrolling for this node
-     *
-     * @param {string=} direction Direction to allow scrolling
-     * Defaults to `all`.
-     * @param {Element=} node Element to apply scroll direction setting.
-     * Defaults to `this`.
-     * @return {void}
-     * @override
-     */
-    setScrollDirection(direction, node) {
-      setTouchAction(
-          /** @type {!Element} */ (node || this),
-          DIRECTION_MAP[direction] || 'auto');
-    }
-    /* **** End Events **** */
-
-    /**
-     * Convenience method to run `querySelector` on this local DOM scope.
-     *
-     * This function calls `Polymer.dom(this.root).querySelector(slctr)`.
-     *
-     * @param {string} slctr Selector to run on this local DOM scope
-     * @return {Element} Element found by the selector, or null if not found.
-     * @override
-     */
-    $$(slctr) {
-      // Note, no need to `wrap` this because root is always patched
-      return this.root.querySelector(slctr);
-    }
-
-    /**
-     * Return the element whose local dom within which this element
-     * is contained. This is a shorthand for
-     * `this.getRootNode().host`.
-     * @this {Element}
-     */
-    get domHost() {
-      let root = wrap(this).getRootNode();
-      return (root instanceof DocumentFragment) ? /** @type {ShadowRoot} */ (root).host : root;
-    }
-
-    /**
-     * Force this element to distribute its children to its local dom.
-     * This should not be necessary as of Polymer 2.0.2 and is provided only
-     * for backwards compatibility.
-     * @return {void}
-     * @override
-     */
-    distributeContent() {
-      const thisEl = /** @type {Element} */ (this);
-      const domApi = /** @type {PolymerDomApi} */(dom(thisEl));
-      if (window.ShadyDOM && domApi.shadowRoot) {
-        ShadyDOM.flush();
-      }
-    }
-
-    /**
-     * Returns a list of nodes that are the effective childNodes. The effective
-     * childNodes list is the same as the element's childNodes except that
-     * any `<content>` elements are replaced with the list of nodes distributed
-     * to the `<content>`, the result of its `getDistributedNodes` method.
-     * @return {!Array<!Node>} List of effective child nodes.
-     * @suppress {invalidCasts} LegacyElementMixin must be applied to an
-     *     HTMLElement
-     * @override
-     */
-    getEffectiveChildNodes() {
-      const thisEl = /** @type {Element} */ (this);
-      const domApi = /** @type {PolymerDomApi} */ (dom(thisEl));
-      return domApi.getEffectiveChildNodes();
-    }
-
-    /**
-     * Returns a list of nodes distributed within this element that match
-     * `selector`. These can be dom children or elements distributed to
-     * children that are insertion points.
-     * @param {string} selector Selector to run.
-     * @return {!Array<!Node>} List of distributed elements that match selector.
-     * @suppress {invalidCasts} LegacyElementMixin must be applied to an
-     * HTMLElement
-     * @override
-     */
-    queryDistributedElements(selector) {
-      const thisEl = /** @type {Element} */ (this);
-      const domApi = /** @type {PolymerDomApi} */ (dom(thisEl));
-      return domApi.queryDistributedElements(selector);
-    }
-
-    /**
-     * Returns a list of elements that are the effective children. The effective
-     * children list is the same as the element's children except that
-     * any `<content>` elements are replaced with the list of elements
-     * distributed to the `<content>`.
-     *
-     * @return {!Array<!Node>} List of effective children.
-     * @override
-     */
-    getEffectiveChildren() {
-      let list = this.getEffectiveChildNodes();
-      return list.filter(function(/** @type {!Node} */ n) {
-        return (n.nodeType === Node.ELEMENT_NODE);
-      });
-    }
-
-    /**
-     * Returns a string of text content that is the concatenation of the
-     * text content's of the element's effective childNodes (the elements
-     * returned by <a href="#getEffectiveChildNodes>getEffectiveChildNodes</a>.
-     *
-     * @return {string} List of effective children.
-     * @override
-     */
-    getEffectiveTextContent() {
-      let cn = this.getEffectiveChildNodes();
-      let tc = [];
-      for (let i=0, c; (c = cn[i]); i++) {
-        if (c.nodeType !== Node.COMMENT_NODE) {
-          tc.push(c.textContent);
-        }
-      }
-      return tc.join('');
-    }
-
-    /**
-     * Returns the first effective childNode within this element that
-     * match `selector`. These can be dom child nodes or elements distributed
-     * to children that are insertion points.
-     * @param {string} selector Selector to run.
-     * @return {Node} First effective child node that matches selector.
-     * @override
-     */
-    queryEffectiveChildren(selector) {
-      let e$ = this.queryDistributedElements(selector);
-      return e$ && e$[0];
-    }
-
-    /**
-     * Returns a list of effective childNodes within this element that
-     * match `selector`. These can be dom child nodes or elements distributed
-     * to children that are insertion points.
-     * @param {string} selector Selector to run.
-     * @return {!Array<!Node>} List of effective child nodes that match
-     *     selector.
-     * @override
-     */
-    queryAllEffectiveChildren(selector) {
-      return this.queryDistributedElements(selector);
-    }
-
-    /**
-     * Returns a list of nodes distributed to this element's `<slot>`.
-     *
-     * If this element contains more than one `<slot>` in its local DOM,
-     * an optional selector may be passed to choose the desired content.
-     *
-     * @param {string=} slctr CSS selector to choose the desired
-     *   `<slot>`.  Defaults to `content`.
-     * @return {!Array<!Node>} List of distributed nodes for the `<slot>`.
-     * @override
-     */
-    getContentChildNodes(slctr) {
-      // Note, no need to `wrap` this because root is always
-      let content = this.root.querySelector(slctr || 'slot');
-      return content ?
-          /** @type {PolymerDomApi} */ (dom(content)).getDistributedNodes() :
-          [];
-    }
-
-    /**
-     * Returns a list of element children distributed to this element's
-     * `<slot>`.
-     *
-     * If this element contains more than one `<slot>` in its
-     * local DOM, an optional selector may be passed to choose the desired
-     * content.  This method differs from `getContentChildNodes` in that only
-     * elements are returned.
-     *
-     * @param {string=} slctr CSS selector to choose the desired
-     *   `<content>`.  Defaults to `content`.
-     * @return {!Array<!HTMLElement>} List of distributed nodes for the
-     *   `<slot>`.
-     * @suppress {invalidCasts}
-     * @override
-     */
-    getContentChildren(slctr) {
-      let children = /** @type {!Array<!HTMLElement>} */(this.getContentChildNodes(slctr).filter(function(n) {
-        return (n.nodeType === Node.ELEMENT_NODE);
-      }));
-      return children;
-    }
-
-    /**
-     * Checks whether an element is in this element's light DOM tree.
-     *
-     * @param {?Node} node The element to be checked.
-     * @return {boolean} true if node is in this element's light DOM tree.
-     * @suppress {invalidCasts} LegacyElementMixin must be applied to an
-     * HTMLElement
-     * @override
-     */
-    isLightDescendant(node) {
-      const thisNode = /** @type {Node} */ (this);
-      return thisNode !== node && wrap(thisNode).contains(node) &&
-        wrap(thisNode).getRootNode() === wrap(node).getRootNode();
-    }
-
-    /**
-     * Checks whether an element is in this element's local DOM tree.
-     *
-     * @param {!Element} node The element to be checked.
-     * @return {boolean} true if node is in this element's local DOM tree.
-     * @override
-     */
-    isLocalDescendant(node) {
-      return this.root === wrap(node).getRootNode();
-    }
-
-    /**
-     * No-op for backwards compatibility. This should now be handled by
-     * ShadyCss library.
-     * @param  {*} container Unused
-     * @param  {*} shouldObserve Unused
-     * @return {void}
-     * @override
-     */
-    scopeSubtree(container, shouldObserve) { // eslint-disable-line no-unused-vars
-    }
-
-    /**
-     * Returns the computed style value for the given property.
-     * @param {string} property The css property name.
-     * @return {string} Returns the computed css property value for the given
-     * `property`.
-     * @suppress {invalidCasts} LegacyElementMixin must be applied to an
-     *     HTMLElement
-     * @override
-     */
-    getComputedStyleValue(property) {
-      return styleInterface.getComputedStyleValue(/** @type {!Element} */(this), property);
-    }
-
-    // debounce
-
-    /**
-     * Call `debounce` to collapse multiple requests for a named task into
-     * one invocation which is made after the wait time has elapsed with
-     * no new request.  If no wait time is given, the callback will be called
-     * at microtask timing (guaranteed before paint).
-     *
-     *     debouncedClickAction(e) {
-     *       // will not call `processClick` more than once per 100ms
-     *       this.debounce('click', function() {
-     *        this.processClick();
-     *       } 100);
-     *     }
-     *
-     * @param {string} jobName String to identify the debounce job.
-     * @param {function():void} callback Function that is called (with `this`
-     *   context) when the wait time elapses.
-     * @param {number=} wait Optional wait time in milliseconds (ms) after the
-     *   last signal that must elapse before invoking `callback`
-     * @return {!Object} Returns a debouncer object on which exists the
-     * following methods: `isActive()` returns true if the debouncer is
-     * active; `cancel()` cancels the debouncer if it is active;
-     * `flush()` immediately invokes the debounced callback if the debouncer
-     * is active.
-     * @override
-     */
-    debounce(jobName, callback, wait) {
-      this._debouncers = this._debouncers || {};
-      return this._debouncers[jobName] = Debouncer.debounce(
-            this._debouncers[jobName]
-          , wait > 0 ? timeOut.after(wait) : microTask
-          , callback.bind(this));
-    }
-
-    /**
-     * Returns whether a named debouncer is active.
-     *
-     * @param {string} jobName The name of the debouncer started with `debounce`
-     * @return {boolean} Whether the debouncer is active (has not yet fired).
-     * @override
-     */
-    isDebouncerActive(jobName) {
-      this._debouncers = this._debouncers || {};
-      let debouncer = this._debouncers[jobName];
-      return !!(debouncer && debouncer.isActive());
-    }
-
-    /**
-     * Immediately calls the debouncer `callback` and inactivates it.
-     *
-     * @param {string} jobName The name of the debouncer started with `debounce`
-     * @return {void}
-     * @override
-     */
-    flushDebouncer(jobName) {
-      this._debouncers = this._debouncers || {};
-      let debouncer = this._debouncers[jobName];
-      if (debouncer) {
-        debouncer.flush();
-      }
-    }
-
-    /**
-     * Cancels an active debouncer.  The `callback` will not be called.
-     *
-     * @param {string} jobName The name of the debouncer started with `debounce`
-     * @return {void}
-     * @override
-     */
-    cancelDebouncer(jobName) {
-      this._debouncers = this._debouncers || {};
-      let debouncer = this._debouncers[jobName];
-      if (debouncer) {
-        debouncer.cancel();
-      }
-    }
-
-    /**
-     * Runs a callback function asynchronously.
-     *
-     * By default (if no waitTime is specified), async callbacks are run at
-     * microtask timing, which will occur before paint.
-     *
-     * @param {!Function} callback The callback function to run, bound to
-     *     `this`.
-     * @param {number=} waitTime Time to wait before calling the
-     *   `callback`.  If unspecified or 0, the callback will be run at microtask
-     *   timing (before paint).
-     * @return {number} Handle that may be used to cancel the async job.
-     * @override
-     */
-    async(callback, waitTime) {
-      return waitTime > 0 ? timeOut.run(callback.bind(this), waitTime) :
-          ~microTask.run(callback.bind(this));
-    }
-
-    /**
-     * Cancels an async operation started with `async`.
-     *
-     * @param {number} handle Handle returned from original `async` call to
-     *   cancel.
-     * @return {void}
-     * @override
-     */
-    cancelAsync(handle) {
-      handle < 0 ? microTask.cancel(~handle) :
-          timeOut.cancel(handle);
-    }
-
-    // other
-
-    /**
-     * Convenience method for creating an element and configuring it.
-     *
-     * @param {string} tag HTML element tag to create.
-     * @param {Object=} props Object of properties to configure on the
-     *    instance.
-     * @return {!Element} Newly created and configured element.
-     * @override
-     */
-    create(tag, props) {
-      let elt = document.createElement(tag);
-      if (props) {
-        if (elt.setProperties) {
-          elt.setProperties(props);
-        } else {
-          for (let n in props) {
-            elt[n] = props[n];
-          }
-        }
-      }
-      return elt;
-    }
-
-    /**
-     * Polyfill for Element.prototype.matches, which is sometimes still
-     * prefixed.
-     *
-     * @param {string} selector Selector to test.
-     * @param {!Element=} node Element to test the selector against.
-     * @return {boolean} Whether the element matches the selector.
-     * @override
-     */
-    elementMatches(selector, node) {
-      return matchesSelector( (node || this), selector);
-    }
-
-    /**
-     * Toggles an HTML attribute on or off.
-     *
-     * @param {string} name HTML attribute name
-     * @param {boolean=} bool Boolean to force the attribute on or off.
-     *    When unspecified, the state of the attribute will be reversed.
-     * @return {boolean} true if the attribute now exists
-     * @override
-     */
-    toggleAttribute(name, bool) {
-      let node = /** @type {Element} */(this);
-      if (arguments.length === 3) {
-        node = /** @type {Element} */(arguments[2]);
-      }
-      if (arguments.length == 1) {
-        bool = !node.hasAttribute(name);
-      }
-      if (bool) {
-        wrap(node).setAttribute(name, '');
-        return true;
-      } else {
-        wrap(node).removeAttribute(name);
-        return false;
-      }
-    }
-
-
-    /**
-     * Toggles a CSS class on or off.
-     *
-     * @param {string} name CSS class name
-     * @param {boolean=} bool Boolean to force the class on or off.
-     *    When unspecified, the state of the class will be reversed.
-     * @param {Element=} node Node to target.  Defaults to `this`.
-     * @return {void}
-     * @override
-     */
-    toggleClass(name, bool, node) {
-      node = /** @type {Element} */ (node || this);
-      if (arguments.length == 1) {
-        bool = !node.classList.contains(name);
-      }
-      if (bool) {
-        node.classList.add(name);
-      } else {
-        node.classList.remove(name);
-      }
-    }
-
-    /**
-     * Cross-platform helper for setting an element's CSS `transform` property.
-     *
-     * @param {string} transformText Transform setting.
-     * @param {Element=} node Element to apply the transform to.
-     * Defaults to `this`
-     * @return {void}
-     * @override
-     */
-    transform(transformText, node) {
-      node = /** @type {Element} */ (node || this);
-      node.style.webkitTransform = transformText;
-      node.style.transform = transformText;
-    }
-
-    /**
-     * Cross-platform helper for setting an element's CSS `translate3d`
-     * property.
-     *
-     * @param {number} x X offset.
-     * @param {number} y Y offset.
-     * @param {number} z Z offset.
-     * @param {Element=} node Element to apply the transform to.
-     * Defaults to `this`.
-     * @return {void}
-     * @override
-     */
-    translate3d(x, y, z, node) {
-      node = /** @type {Element} */ (node || this);
-      this.transform('translate3d(' + x + ',' + y + ',' + z + ')', node);
-    }
-
-    /**
-     * Removes an item from an array, if it exists.
-     *
-     * If the array is specified by path, a change notification is
-     * generated, so that observers, data bindings and computed
-     * properties watching that path can update.
-     *
-     * If the array is passed directly, **no change
-     * notification is generated**.
-     *
-     * @param {string | !Array<number|string>} arrayOrPath Path to array from
-     *     which to remove the item
-     *   (or the array itself).
-     * @param {*} item Item to remove.
-     * @return {Array} Array containing item removed.
-     * @override
-     */
-    arrayDelete(arrayOrPath, item) {
-      let index;
-      if (Array.isArray(arrayOrPath)) {
-        index = arrayOrPath.indexOf(item);
-        if (index >= 0) {
-          return arrayOrPath.splice(index, 1);
-        }
-      } else {
-        let arr = get(this, arrayOrPath);
-        index = arr.indexOf(item);
-        if (index >= 0) {
-          return this.splice(arrayOrPath, index, 1);
-        }
-      }
-      return null;
-    }
-
-    // logging
-
-    /**
-     * Facades `console.log`/`warn`/`error` as override point.
-     *
-     * @param {string} level One of 'log', 'warn', 'error'
-     * @param {Array} args Array of strings or objects to log
-     * @return {void}
-     * @override
-     */
-    _logger(level, args) {
-      // accept ['foo', 'bar'] and [['foo', 'bar']]
-      if (Array.isArray(args) && args.length === 1 && Array.isArray(args[0])) {
-        args = args[0];
-      }
-      switch(level) {
-        case 'log':
-        case 'warn':
-        case 'error':
-          console[level](...args);
-      }
-    }
-
-    /**
-     * Facades `console.log` as an override point.
-     *
-     * @param {...*} args Array of strings or objects to log
-     * @return {void}
-     * @override
-     */
-    _log(...args) {
-      this._logger('log', args);
-    }
-
-    /**
-     * Facades `console.warn` as an override point.
-     *
-     * @param {...*} args Array of strings or objects to log
-     * @return {void}
-     * @override
-     */
-    _warn(...args) {
-      this._logger('warn', args);
-    }
-
-    /**
-     * Facades `console.error` as an override point.
-     *
-     * @param {...*} args Array of strings or objects to log
-     * @return {void}
-     * @override
-     */
-    _error(...args) {
-      this._logger('error', args);
-    }
-
-    /**
-     * Formats a message using the element type an a method name.
-     *
-     * @param {string} methodName Method name to associate with message
-     * @param {...*} args Array of strings or objects to log
-     * @return {Array} Array with formatting information for `console`
-     *   logging.
-     * @override
-     */
-    _logf(methodName, ...args) {
-      return ['[%s::%s]', this.is, methodName, ...args];
-    }
-
-  }
-
-  LegacyElement.prototype.is = '';
-
-  return LegacyElement;
-});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/mutable-data-behavior.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/mutable-data-behavior.js
deleted file mode 100644
index ee23666..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/mutable-data-behavior.js
+++ /dev/null
@@ -1,150 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import { MutableData } from '../mixins/mutable-data.js';
-
-let mutablePropertyChange;
-/** @suppress {missingProperties} */
-(() => {
-  mutablePropertyChange = MutableData._mutablePropertyChange;
-})();
-
-/**
- * Legacy element behavior to skip strict dirty-checking for objects and arrays,
- * (always consider them to be "dirty") for use on legacy API Polymer elements.
- *
- * By default, `Polymer.PropertyEffects` performs strict dirty checking on
- * objects, which means that any deep modifications to an object or array will
- * not be propagated unless "immutable" data patterns are used (i.e. all object
- * references from the root to the mutation were changed).
- *
- * Polymer also provides a proprietary data mutation and path notification API
- * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
- * mutation and notification of deep changes in an object graph to all elements
- * bound to the same object graph.
- *
- * In cases where neither immutable patterns nor the data mutation API can be
- * used, applying this mixin will cause Polymer to skip dirty checking for
- * objects and arrays (always consider them to be "dirty").  This allows a
- * user to make a deep modification to a bound object graph, and then either
- * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
- * (e.g. `this.notifyPath('items')`) to update the tree.  Note that all
- * elements that wish to be updated based on deep mutations must apply this
- * mixin or otherwise skip strict dirty checking for objects/arrays.
- * Specifically, any elements in the binding tree between the source of a
- * mutation and the consumption of it must apply this behavior or enable the
- * `Polymer.OptionalMutableDataBehavior`.
- *
- * In order to make the dirty check strategy configurable, see
- * `Polymer.OptionalMutableDataBehavior`.
- *
- * Note, the performance characteristics of propagating large object graphs
- * will be worse as opposed to using strict dirty checking with immutable
- * patterns or Polymer's path notification API.
- *
- * @polymerBehavior
- * @summary Behavior to skip strict dirty-checking for objects and
- *   arrays
- */
-export const MutableDataBehavior = {
-
-  /**
-   * Overrides `Polymer.PropertyEffects` to provide option for skipping
-   * strict equality checking for Objects and Arrays.
-   *
-   * This method pulls the value to dirty check against from the `__dataTemp`
-   * cache (rather than the normal `__data` cache) for Objects.  Since the temp
-   * cache is cleared at the end of a turn, this implementation allows
-   * side-effects of deep object changes to be processed by re-setting the
-   * same object (using the temp cache as an in-turn backstop to prevent
-   * cycles due to 2-way notification).
-   *
-   * @param {string} property Property name
-   * @param {*} value New property value
-   * @param {*} old Previous property value
-   * @return {boolean} Whether the property should be considered a change
-   * @protected
-   */
-  _shouldPropertyChange(property, value, old) {
-    return mutablePropertyChange(this, property, value, old, true);
-  }
-};
-
-/**
- * Legacy element behavior to add the optional ability to skip strict
- * dirty-checking for objects and arrays (always consider them to be
- * "dirty") by setting a `mutable-data` attribute on an element instance.
- *
- * By default, `Polymer.PropertyEffects` performs strict dirty checking on
- * objects, which means that any deep modifications to an object or array will
- * not be propagated unless "immutable" data patterns are used (i.e. all object
- * references from the root to the mutation were changed).
- *
- * Polymer also provides a proprietary data mutation and path notification API
- * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
- * mutation and notification of deep changes in an object graph to all elements
- * bound to the same object graph.
- *
- * In cases where neither immutable patterns nor the data mutation API can be
- * used, applying this mixin will allow Polymer to skip dirty checking for
- * objects and arrays (always consider them to be "dirty").  This allows a
- * user to make a deep modification to a bound object graph, and then either
- * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
- * (e.g. `this.notifyPath('items')`) to update the tree.  Note that all
- * elements that wish to be updated based on deep mutations must apply this
- * mixin or otherwise skip strict dirty checking for objects/arrays.
- * Specifically, any elements in the binding tree between the source of a
- * mutation and the consumption of it must enable this behavior or apply the
- * `Polymer.OptionalMutableDataBehavior`.
- *
- * While this behavior adds the ability to forgo Object/Array dirty checking,
- * the `mutableData` flag defaults to false and must be set on the instance.
- *
- * Note, the performance characteristics of propagating large object graphs
- * will be worse by relying on `mutableData: true` as opposed to using
- * strict dirty checking with immutable patterns or Polymer's path notification
- * API.
- *
- * @polymerBehavior
- * @summary Behavior to optionally skip strict dirty-checking for objects and
- *   arrays
- */
-export const OptionalMutableDataBehavior = {
-
-  properties: {
-    /**
-     * Instance-level flag for configuring the dirty-checking strategy
-     * for this element.  When true, Objects and Arrays will skip dirty
-     * checking, otherwise strict equality checking will be used.
-     */
-    mutableData: Boolean
-  },
-
-  /**
-   * Overrides `Polymer.PropertyEffects` to skip strict equality checking
-   * for Objects and Arrays.
-   *
-   * Pulls the value to dirty check against from the `__dataTemp` cache
-   * (rather than the normal `__data` cache) for Objects.  Since the temp
-   * cache is cleared at the end of a turn, this implementation allows
-   * side-effects of deep object changes to be processed by re-setting the
-   * same object (using the temp cache as an in-turn backstop to prevent
-   * cycles due to 2-way notification).
-   *
-   * @param {string} property Property name
-   * @param {*} value New property value
-   * @param {*} old Previous property value
-   * @return {boolean} Whether the property should be considered a change
-   * @this {this}
-   * @protected
-   */
-  _shouldPropertyChange(property, value, old) {
-    return mutablePropertyChange(this, property, value, old, this.mutableData);
-  }
-};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer-fn.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer-fn.js
deleted file mode 100644
index de019be6..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer-fn.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import { Class } from './class.js';
-
-import '../utils/boot.js';
-
-/**
- * Legacy class factory and registration helper for defining Polymer
- * elements.
- *
- * This method is equivalent to
- *
- *     import {Class} from '@polymer/polymer/lib/legacy/class.js';
- *     customElements.define(info.is, Class(info));
- *
- * See `Class` for details on valid legacy metadata format for `info`.
- *
- * @global
- * @override
- * @function
- * @param {!PolymerInit} info Object containing Polymer metadata and functions
- *   to become class methods.
- * @return {function(new: HTMLElement)} Generated class
- * @suppress {duplicate, invalidCasts, checkTypes}
- */
-const Polymer = function(info) {
-  // if input is a `class` (aka a function with a prototype), use the prototype
-  // remember that the `constructor` will never be called
-  let klass;
-  if (typeof info === 'function') {
-    klass = info;
-  } else {
-    klass = Polymer.Class(info);
-  }
-  customElements.define(klass.is, /** @type {!HTMLElement} */(klass));
-  return klass;
-};
-
-Polymer.Class = Class;
-
-export { Polymer };
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer.dom.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer.dom.js
deleted file mode 100644
index 6d7f0eb6..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer.dom.js
+++ /dev/null
@@ -1,482 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-import { wrap } from '../utils/wrap.js';
-import '../utils/settings.js';
-import { FlattenedNodesObserver } from '../utils/flattened-nodes-observer.js';
-export { flush, enqueueDebouncer as addDebouncer } from '../utils/flush.js';
-/* eslint-disable no-unused-vars */
-import { Debouncer } from '../utils/debounce.js';  // used in type annotations
-/* eslint-enable no-unused-vars */
-
-const p = Element.prototype;
-/**
- * @const {function(this:Node, string): boolean}
- */
-const normalizedMatchesSelector = p.matches || p.matchesSelector ||
-  p.mozMatchesSelector || p.msMatchesSelector ||
-  p.oMatchesSelector || p.webkitMatchesSelector;
-
-/**
- * Cross-platform `element.matches` shim.
- *
- * @function matchesSelector
- * @param {!Node} node Node to check selector against
- * @param {string} selector Selector to match
- * @return {boolean} True if node matched selector
- */
-export const matchesSelector = function(node, selector) {
-  return normalizedMatchesSelector.call(node, selector);
-};
-
-/**
- * Node API wrapper class returned from `Polymer.dom.(target)` when
- * `target` is a `Node`.
- * @implements {PolymerDomApi}
- * @unrestricted
- */
-class DomApiNative {
-
-  /**
-   * @param {Node} node Node for which to create a Polymer.dom helper object.
-   */
-  constructor(node) {
-    this.node = node;
-  }
-
-  /**
-   * Returns an instance of `FlattenedNodesObserver` that
-   * listens for node changes on this element.
-   *
-   * @param {function(this:HTMLElement, { target: !HTMLElement, addedNodes: !Array<!Element>, removedNodes: !Array<!Element> }):void} callback Called when direct or distributed children
-   *   of this element changes
-   * @return {!PolymerDomApi.ObserveHandle} Observer instance
-   * @override
-   */
-  observeNodes(callback) {
-    return new FlattenedNodesObserver(
-        /** @type {!HTMLElement} */(this.node), callback);
-  }
-
-  /**
-   * Disconnects an observer previously created via `observeNodes`
-   *
-   * @param {!PolymerDomApi.ObserveHandle} observerHandle Observer instance
-   *   to disconnect.
-   * @return {void}
-   * @override
-   */
-  unobserveNodes(observerHandle) {
-    observerHandle.disconnect();
-  }
-
-  /**
-   * Provided as a backwards-compatible API only.  This method does nothing.
-   * @return {void}
-   */
-  notifyObserver() {}
-
-  /**
-   * Returns true if the provided node is contained with this element's
-   * light-DOM children or shadow root, including any nested shadow roots
-   * of children therein.
-   *
-   * @param {Node} node Node to test
-   * @return {boolean} Returns true if the given `node` is contained within
-   *   this element's light or shadow DOM.
-   * @override
-   */
-  deepContains(node) {
-    if (wrap(this.node).contains(node)) {
-      return true;
-    }
-    let n = node;
-    let doc = node.ownerDocument;
-    // walk from node to `this` or `document`
-    while (n && n !== doc && n !== this.node) {
-      // use logical parentnode, or native ShadowRoot host
-      n = wrap(n).parentNode || wrap(n).host;
-    }
-    return n === this.node;
-  }
-
-  /**
-   * Returns the root node of this node.  Equivalent to `getRootNode()`.
-   *
-   * @return {Node} Top most element in the dom tree in which the node
-   * exists. If the node is connected to a document this is either a
-   * shadowRoot or the document; otherwise, it may be the node
-   * itself or a node or document fragment containing it.
-   * @override
-   */
-  getOwnerRoot() {
-    return wrap(this.node).getRootNode();
-  }
-
-  /**
-   * For slot elements, returns the nodes assigned to the slot; otherwise
-   * an empty array. It is equivalent to `<slot>.addignedNodes({flatten:true})`.
-   *
-   * @return {!Array<!Node>} Array of assigned nodes
-   * @override
-   */
-  getDistributedNodes() {
-    return (this.node.localName === 'slot') ?
-      wrap(this.node).assignedNodes({flatten: true}) :
-      [];
-  }
-
-  /**
-   * Returns an array of all slots this element was distributed to.
-   *
-   * @return {!Array<!HTMLSlotElement>} Description
-   * @override
-   */
-  getDestinationInsertionPoints() {
-    let ip$ = [];
-    let n = wrap(this.node).assignedSlot;
-    while (n) {
-      ip$.push(n);
-      n = wrap(n).assignedSlot;
-    }
-    return ip$;
-  }
-
-  /**
-   * Calls `importNode` on the `ownerDocument` for this node.
-   *
-   * @param {!Node} node Node to import
-   * @param {boolean} deep True if the node should be cloned deeply during
-   *   import
-   * @return {Node} Clone of given node imported to this owner document
-   */
-  importNode(node, deep) {
-    let doc = this.node instanceof Document ? this.node :
-      this.node.ownerDocument;
-    return wrap(doc).importNode(node, deep);
-  }
-
-  /**
-   * @return {!Array<!Node>} Returns a flattened list of all child nodes and
-   * nodes assigned to child slots.
-   * @override
-   */
-  getEffectiveChildNodes() {
-    return FlattenedNodesObserver.getFlattenedNodes(
-        /** @type {!HTMLElement} */ (this.node));
-  }
-
-  /**
-   * Returns a filtered list of flattened child elements for this element based
-   * on the given selector.
-   *
-   * @param {string} selector Selector to filter nodes against
-   * @return {!Array<!HTMLElement>} List of flattened child elements
-   * @override
-   */
-  queryDistributedElements(selector) {
-    let c$ = this.getEffectiveChildNodes();
-    let list = [];
-    for (let i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
-      if ((c.nodeType === Node.ELEMENT_NODE) &&
-          matchesSelector(c, selector)) {
-        list.push(c);
-      }
-    }
-    return list;
-  }
-
-  /**
-   * For shadow roots, returns the currently focused element within this
-   * shadow root.
-   *
-   * return {Node|undefined} Currently focused element
-   * @override
-   */
-  get activeElement() {
-    let node = this.node;
-    return node._activeElement !== undefined ? node._activeElement : node.activeElement;
-  }
-}
-
-function forwardMethods(proto, methods) {
-  for (let i=0; i < methods.length; i++) {
-    let method = methods[i];
-    /* eslint-disable valid-jsdoc */
-    proto[method] = /** @this {DomApiNative} */ function() {
-      return this.node[method].apply(this.node, arguments);
-    };
-    /* eslint-enable */
-  }
-}
-
-function forwardReadOnlyProperties(proto, properties) {
-  for (let i=0; i < properties.length; i++) {
-    let name = properties[i];
-    Object.defineProperty(proto, name, {
-      get: function() {
-        const domApi = /** @type {DomApiNative} */(this);
-        return domApi.node[name];
-      },
-      configurable: true
-    });
-  }
-}
-
-function forwardProperties(proto, properties) {
-  for (let i=0; i < properties.length; i++) {
-    let name = properties[i];
-    Object.defineProperty(proto, name, {
-      /**
-       * @this {DomApiNative}
-       * @return {*} .
-       */
-      get: function() {
-        return this.node[name];
-      },
-      /**
-       * @this {DomApiNative}
-       * @param {*} value .
-       */
-      set: function(value) {
-        this.node[name] = value;
-      },
-      configurable: true
-    });
-  }
-}
-
-
-/**
- * Event API wrapper class returned from `dom.(target)` when
- * `target` is an `Event`.
- */
-export class EventApi {
-  constructor(event) {
-    this.event = event;
-  }
-
-  /**
-   * Returns the first node on the `composedPath` of this event.
-   *
-   * @return {!EventTarget} The node this event was dispatched to
-   */
-  get rootTarget() {
-    return this.path[0];
-  }
-
-  /**
-   * Returns the local (re-targeted) target for this event.
-   *
-   * @return {!EventTarget} The local (re-targeted) target for this event.
-   */
-  get localTarget() {
-    return this.event.target;
-  }
-
-  /**
-   * Returns the `composedPath` for this event.
-   * @return {!Array<!EventTarget>} The nodes this event propagated through
-   */
-  get path() {
-    return this.event.composedPath();
-  }
-}
-
-/**
- * @function
- * @param {boolean=} deep
- * @return {!Node}
- */
-DomApiNative.prototype.cloneNode;
-/**
- * @function
- * @param {!Node} node
- * @return {!Node}
- */
-DomApiNative.prototype.appendChild;
-/**
- * @function
- * @param {!Node} newChild
- * @param {Node} refChild
- * @return {!Node}
- */
-DomApiNative.prototype.insertBefore;
-/**
- * @function
- * @param {!Node} node
- * @return {!Node}
- */
-DomApiNative.prototype.removeChild;
-/**
- * @function
- * @param {!Node} oldChild
- * @param {!Node} newChild
- * @return {!Node}
- */
-DomApiNative.prototype.replaceChild;
-/**
- * @function
- * @param {string} name
- * @param {string} value
- * @return {void}
- */
-DomApiNative.prototype.setAttribute;
-/**
- * @function
- * @param {string} name
- * @return {void}
- */
-DomApiNative.prototype.removeAttribute;
-/**
- * @function
- * @param {string} selector
- * @return {?Element}
- */
-DomApiNative.prototype.querySelector;
-/**
- * @function
- * @param {string} selector
- * @return {!NodeList<!Element>}
- */
-DomApiNative.prototype.querySelectorAll;
-
-/** @type {?Node} */
-DomApiNative.prototype.parentNode;
-/** @type {?Node} */
-DomApiNative.prototype.firstChild;
-/** @type {?Node} */
-DomApiNative.prototype.lastChild;
-/** @type {?Node} */
-DomApiNative.prototype.nextSibling;
-/** @type {?Node} */
-DomApiNative.prototype.previousSibling;
-/** @type {?HTMLElement} */
-DomApiNative.prototype.firstElementChild;
-/** @type {?HTMLElement} */
-DomApiNative.prototype.lastElementChild;
-/** @type {?HTMLElement} */
-DomApiNative.prototype.nextElementSibling;
-/** @type {?HTMLElement} */
-DomApiNative.prototype.previousElementSibling;
-/** @type {!Array<!Node>} */
-DomApiNative.prototype.childNodes;
-/** @type {!Array<!HTMLElement>} */
-DomApiNative.prototype.children;
-/** @type {?DOMTokenList} */
-DomApiNative.prototype.classList;
-
-/** @type {string} */
-DomApiNative.prototype.textContent;
-/** @type {string} */
-DomApiNative.prototype.innerHTML;
-
-let DomApiImpl = DomApiNative;
-
-if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noPatch'] && window['ShadyDOM']['Wrapper']) {
-
-  /**
-   * @private
-   * @extends {HTMLElement}
-   */
-  class Wrapper extends window['ShadyDOM']['Wrapper'] {}
-
-  // copy bespoke API onto wrapper
-  Object.getOwnPropertyNames(DomApiNative.prototype).forEach((prop) => {
-    if (prop != 'activeElement') {
-      Wrapper.prototype[prop] = DomApiNative.prototype[prop];
-    }
-  });
-
-  // Note, `classList` is here only for legacy compatibility since it does not
-  // trigger distribution in v1 Shadow DOM.
-  forwardReadOnlyProperties(Wrapper.prototype, [
-    'classList'
-  ]);
-
-  DomApiImpl = Wrapper;
-
-  Object.defineProperties(EventApi.prototype, {
-
-    localTarget: {
-      get() {
-        return this.event.currentTarget;
-      },
-      configurable: true
-    },
-
-    path: {
-      get() {
-        return window['ShadyDOM']['composedPath'](this.event);
-      },
-      configurable: true
-    }
-  });
-
-} else {
-
-  // Methods that can provoke distribution or must return the logical, not
-  // composed tree.
-  forwardMethods(DomApiNative.prototype, [
-    'cloneNode', 'appendChild', 'insertBefore', 'removeChild',
-    'replaceChild', 'setAttribute', 'removeAttribute',
-    'querySelector', 'querySelectorAll'
-  ]);
-
-  // Properties that should return the logical, not composed tree. Note, `classList`
-  // is here only for legacy compatibility since it does not trigger distribution
-  // in v1 Shadow DOM.
-  forwardReadOnlyProperties(DomApiNative.prototype, [
-    'parentNode', 'firstChild', 'lastChild',
-    'nextSibling', 'previousSibling', 'firstElementChild',
-    'lastElementChild', 'nextElementSibling', 'previousElementSibling',
-    'childNodes', 'children', 'classList'
-  ]);
-
-  forwardProperties(DomApiNative.prototype, [
-    'textContent', 'innerHTML'
-  ]);
-}
-
-export const DomApi = DomApiImpl;
-
-/**
- * Legacy DOM and Event manipulation API wrapper factory used to abstract
- * differences between native Shadow DOM and "Shady DOM" when polyfilling on
- * older browsers.
- *
- * Note that in Polymer 2.x use of `Polymer.dom` is no longer required and
- * in the majority of cases simply facades directly to the standard native
- * API.
- *
- * @summary Legacy DOM and Event manipulation API wrapper factory used to
- * abstract differences between native Shadow DOM and "Shady DOM."
- * @param {(Node|Event|DomApiNative|EventApi)=} obj Node or event to operate on
- * @return {!DomApiNative|!EventApi} Wrapper providing either node API or event API
- */
-export const dom = function(obj) {
-  obj = obj || document;
-  if (obj instanceof DomApiImpl) {
-    return /** @type {!DomApi} */(obj);
-  }
-  if (obj instanceof EventApi) {
-    return /** @type {!EventApi} */(obj);
-  }
-  let helper = obj['__domApi'];
-  if (!helper) {
-    if (obj instanceof Event) {
-      helper = new EventApi(obj);
-    } else {
-      helper = new DomApiImpl(/** @type {Node} */(obj));
-    }
-    obj['__domApi'] = helper;
-  }
-  return helper;
-};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/templatizer-behavior.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/templatizer-behavior.js
deleted file mode 100644
index 2dfbbc3..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/templatizer-behavior.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import { TemplateInstanceBase, templatize, modelForElement } from '../utils/templatize.js'; // eslint-disable-line no-unused-vars
-
-/**
- * @typedef {{
- *   _templatizerTemplate: HTMLTemplateElement,
- *   _parentModel: boolean,
- *   _instanceProps: Object,
- *   _forwardHostPropV2: Function,
- *   _notifyInstancePropV2: Function,
- *   ctor: TemplateInstanceBase
- * }}
- */
-let TemplatizerUser; // eslint-disable-line
-
-/**
- * The `Templatizer` behavior adds methods to generate instances of
- * templates that are each managed by an anonymous `PropertyEffects`
- * instance where data-bindings in the stamped template content are bound to
- * accessors on itself.
- *
- * This behavior is provided in Polymer 2.x-3.x as a hybrid-element convenience
- * only.  For non-hybrid usage, the `Templatize` library
- * should be used instead.
- *
- * Example:
- *
- *     import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
- *     // Get a template from somewhere, e.g. light DOM
- *     let template = this.querySelector('template');
- *     // Prepare the template
- *     this.templatize(template);
- *     // Instance the template with an initial data model
- *     let instance = this.stamp({myProp: 'initial'});
- *     // Insert the instance's DOM somewhere, e.g. light DOM
- *     dom(this).appendChild(instance.root);
- *     // Changing a property on the instance will propagate to bindings
- *     // in the template
- *     instance.myProp = 'new value';
- *
- * Users of `Templatizer` may need to implement the following abstract
- * API's to determine how properties and paths from the host should be
- * forwarded into to instances:
- *
- *     _forwardHostPropV2: function(prop, value)
- *
- * Likewise, users may implement these additional abstract API's to determine
- * how instance-specific properties that change on the instance should be
- * forwarded out to the host, if necessary.
- *
- *     _notifyInstancePropV2: function(inst, prop, value)
- *
- * In order to determine which properties are instance-specific and require
- * custom notification via `_notifyInstanceProp`, define an `_instanceProps`
- * object containing keys for each instance prop, for example:
- *
- *     _instanceProps: {
- *       item: true,
- *       index: true
- *     }
- *
- * Any properties used in the template that are not defined in _instanceProp
- * will be forwarded out to the Templatize `owner` automatically.
- *
- * Users may also implement the following abstract function to show or
- * hide any DOM generated using `stamp`:
- *
- *     _showHideChildren: function(shouldHide)
- *
- * Note that some callbacks are suffixed with `V2` in the Polymer 2.x behavior
- * as the implementations will need to differ from the callbacks required
- * by the 1.x Templatizer API due to changes in the `TemplateInstance` API
- * between versions 1.x and 2.x.
- *
- * @polymerBehavior
- */
-export const Templatizer = {
-
-  /**
-   * Generates an anonymous `TemplateInstance` class (stored as `this.ctor`)
-   * for the provided template.  This method should be called once per
-   * template to prepare an element for stamping the template, followed
-   * by `stamp` to create new instances of the template.
-   *
-   * @param {!HTMLTemplateElement} template Template to prepare
-   * @param {boolean=} mutableData When `true`, the generated class will skip
-   *   strict dirty-checking for objects and arrays (always consider them to
-   *   be "dirty"). Defaults to false.
-   * @return {void}
-   * @this {TemplatizerUser}
-   */
-  templatize(template, mutableData) {
-    this._templatizerTemplate = template;
-    this.ctor = templatize(template, this, {
-      mutableData: Boolean(mutableData),
-      parentModel: this._parentModel,
-      instanceProps: this._instanceProps,
-      forwardHostProp: this._forwardHostPropV2,
-      notifyInstanceProp: this._notifyInstancePropV2
-    });
-  },
-
-  /**
-   * Creates an instance of the template prepared by `templatize`.  The object
-   * returned is an instance of the anonymous class generated by `templatize`
-   * whose `root` property is a document fragment containing newly cloned
-   * template content, and which has property accessors corresponding to
-   * properties referenced in template bindings.
-   *
-   * @param {Object=} model Object containing initial property values to
-   *   populate into the template bindings.
-   * @return {TemplateInstanceBase} Returns the created instance of
-   * the template prepared by `templatize`.
-   * @this {TemplatizerUser}
-   */
-  stamp(model) {
-    return new this.ctor(model);
-  },
-
-  /**
-   * Returns the template "model" (`TemplateInstance`) associated with
-   * a given element, which serves as the binding scope for the template
-   * instance the element is contained in.  A template model should be used
-   * to manipulate data associated with this template instance.
-   *
-   * @param {HTMLElement} el Element for which to return a template model.
-   * @return {TemplateInstanceBase} Model representing the binding scope for
-   *   the element.
-   * @this {TemplatizerUser}
-   */
-  modelForElement(el) {
-    return modelForElement(this._templatizerTemplate, el);
-  }
-};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/dir-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/dir-mixin.js
deleted file mode 100644
index 3237511..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/dir-mixin.js
+++ /dev/null
@@ -1,195 +0,0 @@
-/**
- * @fileoverview
- * @suppress {checkPrototypalTypes}
- * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at
- * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
- * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
- * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
- * Google as part of the polymer project is also subject to an additional IP
- * rights grant found at http://polymer.github.io/PATENTS.txt
- */
-import { PropertyAccessors } from './property-accessors.js';
-
-import { dedupingMixin } from '../utils/mixin.js';
-
-const HOST_DIR = /:host\(:dir\((ltr|rtl)\)\)/g;
-const HOST_DIR_REPLACMENT = ':host([dir="$1"])';
-
-const EL_DIR = /([\s\w-#\.\[\]\*]*):dir\((ltr|rtl)\)/g;
-const EL_DIR_REPLACMENT = ':host([dir="$2"]) $1';
-
-const DIR_CHECK = /:dir\((?:ltr|rtl)\)/;
-
-const SHIM_SHADOW = Boolean(window['ShadyDOM'] && window['ShadyDOM']['inUse']);
-
-/**
- * @type {!Array<!Polymer_DirMixin>}
- */
-const DIR_INSTANCES = [];
-
-/** @type {?MutationObserver} */
-let observer = null;
-
-let DOCUMENT_DIR = '';
-
-function getRTL() {
-  DOCUMENT_DIR = document.documentElement.getAttribute('dir');
-}
-
-/**
- * @param {!Polymer_DirMixin} instance Instance to set RTL status on
- */
-function setRTL(instance) {
-  if (!instance.__autoDirOptOut) {
-    const el = /** @type {!HTMLElement} */(instance);
-    el.setAttribute('dir', DOCUMENT_DIR);
-  }
-}
-
-function updateDirection() {
-  getRTL();
-  DOCUMENT_DIR = document.documentElement.getAttribute('dir');
-  for (let i = 0; i < DIR_INSTANCES.length; i++) {
-    setRTL(DIR_INSTANCES[i]);
-  }
-}
-
-function takeRecords() {
-  if (observer && observer.takeRecords().length) {
-    updateDirection();
-  }
-}
-
-/**
- * Element class mixin that allows elements to use the `:dir` CSS Selector to
- * have text direction specific styling.
- *
- * With this mixin, any stylesheet provided in the template will transform
- * `:dir` into `:host([dir])` and sync direction with the page via the
- * element's `dir` attribute.
- *
- * Elements can opt out of the global page text direction by setting the `dir`
- * attribute directly in `ready()` or in HTML.
- *
- * Caveats:
- * - Applications must set `<html dir="ltr">` or `<html dir="rtl">` to sync
- *   direction
- * - Automatic left-to-right or right-to-left styling is sync'd with the
- *   `<html>` element only.
- * - Changing `dir` at runtime is supported.
- * - Opting out of the global direction styling is permanent
- *
- * @mixinFunction
- * @polymer
- * @appliesMixin PropertyAccessors
- */
-export const DirMixin = dedupingMixin((base) => {
-
-  if (!SHIM_SHADOW) {
-    if (!observer) {
-      getRTL();
-      observer = new MutationObserver(updateDirection);
-      observer.observe(document.documentElement, {attributes: true, attributeFilter: ['dir']});
-    }
-  }
-
-  /**
-   * @constructor
-   * @implements {Polymer_PropertyAccessors}
-   * @private
-   */
-  const elementBase = PropertyAccessors(base);
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_DirMixin}
-   */
-  class Dir extends elementBase {
-
-    /**
-     * @param {string} cssText .
-     * @param {string} baseURI .
-     * @return {string} .
-     * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
-     */
-    static _processStyleText(cssText, baseURI) {
-      cssText = super._processStyleText(cssText, baseURI);
-      if (!SHIM_SHADOW && DIR_CHECK.test(cssText)) {
-        cssText = this._replaceDirInCssText(cssText);
-        this.__activateDir = true;
-      }
-      return cssText;
-    }
-
-    /**
-     * Replace `:dir` in the given CSS text
-     *
-     * @param {string} text CSS text to replace DIR
-     * @return {string} Modified CSS
-     */
-    static _replaceDirInCssText(text) {
-      let replacedText = text;
-      replacedText = replacedText.replace(HOST_DIR, HOST_DIR_REPLACMENT);
-      replacedText = replacedText.replace(EL_DIR, EL_DIR_REPLACMENT);
-      return replacedText;
-    }
-
-    constructor() {
-      super();
-      /** @type {boolean} */
-      this.__autoDirOptOut = false;
-    }
-
-    /**
-     * @override
-     * @suppress {invalidCasts} Closure doesn't understand that `this` is an
-     *     HTMLElement
-     * @return {void}
-     */
-    ready() {
-      super.ready();
-      this.__autoDirOptOut = /** @type {!HTMLElement} */(this).hasAttribute('dir');
-    }
-
-    /**
-     * @override
-     * @suppress {missingProperties} If it exists on elementBase, it can be
-     *   super'd
-     * @return {void}
-     */
-    connectedCallback() {
-      if (elementBase.prototype.connectedCallback) {
-        super.connectedCallback();
-      }
-      if (this.constructor.__activateDir) {
-        takeRecords();
-        DIR_INSTANCES.push(this);
-        setRTL(this);
-      }
-    }
-
-    /**
-     * @override
-     * @suppress {missingProperties} If it exists on elementBase, it can be
-     *   super'd
-     * @return {void}
-     */
-    disconnectedCallback() {
-      if (elementBase.prototype.disconnectedCallback) {
-        super.disconnectedCallback();
-      }
-      if (this.constructor.__activateDir) {
-        const idx = DIR_INSTANCES.indexOf(this);
-        if (idx > -1) {
-          DIR_INSTANCES.splice(idx, 1);
-        }
-      }
-    }
-  }
-
-  Dir.__activateDir = false;
-
-  return Dir;
-});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/disable-upgrade-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/disable-upgrade-mixin.js
deleted file mode 100644
index a97aa9e9..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/disable-upgrade-mixin.js
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * @fileoverview
- * @suppress {checkPrototypalTypes}
- * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at
- * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
- * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
- * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
- * Google as part of the polymer project is also subject to an additional IP
- * rights grant found at http://polymer.github.io/PATENTS.txt
- */
-import { ElementMixin } from './element-mixin.js';
-
-import { dedupingMixin } from '../utils/mixin.js';
-
-const DISABLED_ATTR = 'disable-upgrade';
-
-/**
- * Element class mixin that allows the element to boot up in a non-enabled
- * state when the `disable-upgrade` attribute is present. This mixin is
- * designed to be used with element classes like PolymerElement that perform
- * initial startup work when they are first connected. When the
- * `disable-upgrade` attribute is removed, if the element is connected, it
- * boots up and "enables" as it otherwise would; if it is not connected, the
- * element boots up when it is next connected.
- *
- * Using `disable-upgrade` with PolymerElement prevents any data propagation
- * to the element, any element DOM from stamping, or any work done in
- * connected/disconnctedCallback from occuring, but it does not prevent work
- * done in the element constructor.
- *
- * Note, this mixin must be applied on top of any element class that
- * itself implements a `connectedCallback` so that it can control the work
- * done in `connectedCallback`. For example,
- *
- *     MyClass = DisableUpgradeMixin(class extends BaseClass {...});
- *
- * @mixinFunction
- * @polymer
- * @appliesMixin ElementMixin
- */
-export const DisableUpgradeMixin = dedupingMixin((base) => {
-  /**
-   * @constructor
-   * @implements {Polymer_ElementMixin}
-   * @extends {HTMLElement}
-   * @private
-   */
-  const superClass = ElementMixin(base);
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_DisableUpgradeMixin}
-   */
-  class DisableUpgradeClass extends superClass {
-
-    /**
-     * @suppress {missingProperties} go/missingfnprops
-     */
-    static get observedAttributes() {
-      return super.observedAttributes.concat(DISABLED_ATTR);
-    }
-
-    /**
-     * @override
-     * @param {string} name Attribute name.
-     * @param {?string} old The previous value for the attribute.
-     * @param {?string} value The new value for the attribute.
-     * @param {?string=} namespace The XML namespace for the attribute.
-     * @return {undefined}
-     */
-    attributeChangedCallback(name, old, value, namespace) {
-      if (name == DISABLED_ATTR) {
-        if (!this.__dataEnabled && value == null && this.isConnected) {
-          super.connectedCallback();
-        }
-      } else {
-        super.attributeChangedCallback(
-            name, old, value, /** @type {null|string} */ (namespace));
-      }
-    }
-
-    /*
-      NOTE: cannot gate on attribute because this is called before
-      attributes are delivered. Therefore, we stub this out and
-      call `super._initializeProperties()` manually.
-    */
-    /** @override */
-    _initializeProperties() {}
-
-    // prevent user code in connected from running
-    /** @override */
-    connectedCallback() {
-      if (this.__dataEnabled || !this.hasAttribute(DISABLED_ATTR)) {
-        super.connectedCallback();
-      }
-    }
-
-    // prevent element from turning on properties
-    /** @override */
-    _enableProperties() {
-      if (!this.hasAttribute(DISABLED_ATTR)) {
-        if (!this.__dataEnabled) {
-          super._initializeProperties();
-        }
-        super._enableProperties();
-      }
-    }
-
-    // only go if "enabled"
-    /** @override */
-    disconnectedCallback() {
-      if (this.__dataEnabled) {
-        super.disconnectedCallback();
-      }
-    }
-
-  }
-
-  return DisableUpgradeClass;
-});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/element-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/element-mixin.js
deleted file mode 100644
index af291ac..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/element-mixin.js
+++ /dev/null
@@ -1,813 +0,0 @@
-/**
- * @fileoverview
- * @suppress {checkPrototypalTypes}
- * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at
- * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
- * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
- * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
- * Google as part of the polymer project is also subject to an additional IP
- * rights grant found at http://polymer.github.io/PATENTS.txt
- */
-import '../utils/boot.js';
-
-import { rootPath, strictTemplatePolicy, allowTemplateFromDomModule, legacyOptimizations, syncInitialRender } from '../utils/settings.js';
-import { dedupingMixin } from '../utils/mixin.js';
-import { stylesFromTemplate, stylesFromModuleImports } from '../utils/style-gather.js';
-import { pathFromUrl, resolveCss, resolveUrl } from '../utils/resolve-url.js';
-import { DomModule } from '../elements/dom-module.js';
-import { PropertyEffects } from './property-effects.js';
-import { PropertiesMixin } from './properties-mixin.js';
-import { wrap } from '../utils/wrap.js';
-
-/**
- * Current Polymer version in Semver notation.
- * @type {string} Semver notation of the current version of Polymer.
- */
-export const version = '3.2.0';
-
-const builtCSS = window.ShadyCSS && window.ShadyCSS['cssBuild'];
-
-/**
- * Element class mixin that provides the core API for Polymer's meta-programming
- * features including template stamping, data-binding, attribute deserialization,
- * and property change observation.
- *
- * Subclassers may provide the following static getters to return metadata
- * used to configure Polymer's features for the class:
- *
- * - `static get is()`: When the template is provided via a `dom-module`,
- *   users should return the `dom-module` id from a static `is` getter.  If
- *   no template is needed or the template is provided directly via the
- *   `template` getter, there is no need to define `is` for the element.
- *
- * - `static get template()`: Users may provide the template directly (as
- *   opposed to via `dom-module`) by implementing a static `template` getter.
- *   The getter must return an `HTMLTemplateElement`.
- *
- * - `static get properties()`: Should return an object describing
- *   property-related metadata used by Polymer features (key: property name
- *   value: object containing property metadata). Valid keys in per-property
- *   metadata include:
- *   - `type` (String|Number|Object|Array|...): Used by
- *     `attributeChangedCallback` to determine how string-based attributes
- *     are deserialized to JavaScript property values.
- *   - `notify` (boolean): Causes a change in the property to fire a
- *     non-bubbling event called `<property>-changed`. Elements that have
- *     enabled two-way binding to the property use this event to observe changes.
- *   - `readOnly` (boolean): Creates a getter for the property, but no setter.
- *     To set a read-only property, use the private setter method
- *     `_setProperty(property, value)`.
- *   - `observer` (string): Observer method name that will be called when
- *     the property changes. The arguments of the method are
- *     `(value, previousValue)`.
- *   - `computed` (string): String describing method and dependent properties
- *     for computing the value of this property (e.g. `'computeFoo(bar, zot)'`).
- *     Computed properties are read-only by default and can only be changed
- *     via the return value of the computing method.
- *
- * - `static get observers()`: Array of strings describing multi-property
- *   observer methods and their dependent properties (e.g.
- *   `'observeABC(a, b, c)'`).
- *
- * The base class provides default implementations for the following standard
- * custom element lifecycle callbacks; users may override these, but should
- * call the super method to ensure
- * - `constructor`: Run when the element is created or upgraded
- * - `connectedCallback`: Run each time the element is connected to the
- *   document
- * - `disconnectedCallback`: Run each time the element is disconnected from
- *   the document
- * - `attributeChangedCallback`: Run each time an attribute in
- *   `observedAttributes` is set or removed (note: this element's default
- *   `observedAttributes` implementation will automatically return an array
- *   of dash-cased attributes based on `properties`)
- *
- * @mixinFunction
- * @polymer
- * @appliesMixin PropertyEffects
- * @appliesMixin PropertiesMixin
- * @property rootPath {string} Set to the value of `rootPath`,
- *   which defaults to the main document path
- * @property importPath {string} Set to the value of the class's static
- *   `importPath` property, which defaults to the path of this element's
- *   `dom-module` (when `is` is used), but can be overridden for other
- *   import strategies.
- * @summary Element class mixin that provides the core API for Polymer's
- * meta-programming features.
- */
-export const ElementMixin = dedupingMixin(base => {
-  /**
-   * @constructor
-   * @implements {Polymer_PropertyEffects}
-   * @implements {Polymer_PropertiesMixin}
-   * @extends {HTMLElement}
-   * @private
-   */
-  const polymerElementBase = PropertiesMixin(PropertyEffects(base));
-
-  /**
-   * Returns a list of properties with default values.
-   * This list is created as an optimization since it is a subset of
-   * the list returned from `_properties`.
-   * This list is used in `_initializeProperties` to set property defaults.
-   *
-   * @param {PolymerElementConstructor} constructor Element class
-   * @return {PolymerElementProperties} Flattened properties for this class
-   *   that have default values
-   * @private
-   */
-  function propertyDefaults(constructor) {
-    if (!constructor.hasOwnProperty(
-      JSCompiler_renameProperty('__propertyDefaults', constructor))) {
-      constructor.__propertyDefaults = null;
-      let props = constructor._properties;
-      for (let p in props) {
-        let info = props[p];
-        if ('value' in info) {
-          constructor.__propertyDefaults = constructor.__propertyDefaults || {};
-          constructor.__propertyDefaults[p] = info;
-        }
-      }
-    }
-    return constructor.__propertyDefaults;
-  }
-
-  /**
-   * Returns a memoized version of the `observers` array.
-   * @param {PolymerElementConstructor} constructor Element class
-   * @return {Array} Array containing own observers for the given class
-   * @protected
-   */
-  function ownObservers(constructor) {
-    if (!constructor.hasOwnProperty(
-      JSCompiler_renameProperty('__ownObservers', constructor))) {
-      constructor.__ownObservers =
-          constructor.hasOwnProperty(
-              JSCompiler_renameProperty('observers', constructor)) ?
-          /** @type {PolymerElementConstructor} */ (constructor).observers :
-          null;
-    }
-    return constructor.__ownObservers;
-  }
-
-  /**
-   * Creates effects for a property.
-   *
-   * Note, once a property has been set to
-   * `readOnly`, `computed`, `reflectToAttribute`, or `notify`
-   * these values may not be changed. For example, a subclass cannot
-   * alter these settings. However, additional `observers` may be added
-   * by subclasses.
-   *
-   * The info object should contain property metadata as follows:
-   *
-   * * `type`: {function} type to which an attribute matching the property
-   * is deserialized. Note the property is camel-cased from a dash-cased
-   * attribute. For example, 'foo-bar' attribute is deserialized to a
-   * property named 'fooBar'.
-   *
-   * * `readOnly`: {boolean} creates a readOnly property and
-   * makes a private setter for the private of the form '_setFoo' for a
-   * property 'foo',
-   *
-   * * `computed`: {string} creates a computed property. A computed property
-   * is also automatically set to `readOnly: true`. The value is calculated
-   * by running a method and arguments parsed from the given string. For
-   * example 'compute(foo)' will compute a given property when the
-   * 'foo' property changes by executing the 'compute' method. This method
-   * must return the computed value.
-   *
-   * * `reflectToAttribute`: {boolean} If true, the property value is reflected
-   * to an attribute of the same name. Note, the attribute is dash-cased
-   * so a property named 'fooBar' is reflected as 'foo-bar'.
-   *
-   * * `notify`: {boolean} sends a non-bubbling notification event when
-   * the property changes. For example, a property named 'foo' sends an
-   * event named 'foo-changed' with `event.detail` set to the value of
-   * the property.
-   *
-   * * observer: {string} name of a method that runs when the property
-   * changes. The arguments of the method are (value, previousValue).
-   *
-   * Note: Users may want control over modifying property
-   * effects via subclassing. For example, a user might want to make a
-   * reflectToAttribute property not do so in a subclass. We've chosen to
-   * disable this because it leads to additional complication.
-   * For example, a readOnly effect generates a special setter. If a subclass
-   * disables the effect, the setter would fail unexpectedly.
-   * Based on feedback, we may want to try to make effects more malleable
-   * and/or provide an advanced api for manipulating them.
-   *
-   * @param {!PolymerElement} proto Element class prototype to add accessors
-   *   and effects to
-   * @param {string} name Name of the property.
-   * @param {Object} info Info object from which to create property effects.
-   * Supported keys:
-   * @param {Object} allProps Flattened map of all properties defined in this
-   *   element (including inherited properties)
-   * @return {void}
-   * @private
-   */
-  function createPropertyFromConfig(proto, name, info, allProps) {
-    // computed forces readOnly...
-    if (info.computed) {
-      info.readOnly = true;
-    }
-    // Note, since all computed properties are readOnly, this prevents
-    // adding additional computed property effects (which leads to a confusing
-    // setup where multiple triggers for setting a property)
-    // While we do have `hasComputedEffect` this is set on the property's
-    // dependencies rather than itself.
-    if (info.computed) {
-      if (proto._hasReadOnlyEffect(name)) {
-        console.warn(`Cannot redefine computed property '${name}'.`);
-      } else {
-        proto._createComputedProperty(name, info.computed, allProps);
-      }
-    }
-    if (info.readOnly && !proto._hasReadOnlyEffect(name)) {
-      proto._createReadOnlyProperty(name, !info.computed);
-    } else if (info.readOnly === false && proto._hasReadOnlyEffect(name)) {
-      console.warn(`Cannot make readOnly property '${name}' non-readOnly.`);
-    }
-    if (info.reflectToAttribute && !proto._hasReflectEffect(name)) {
-      proto._createReflectedProperty(name);
-    } else if (info.reflectToAttribute === false && proto._hasReflectEffect(name)) {
-      console.warn(`Cannot make reflected property '${name}' non-reflected.`);
-    }
-    if (info.notify && !proto._hasNotifyEffect(name)) {
-      proto._createNotifyingProperty(name);
-    } else if (info.notify === false && proto._hasNotifyEffect(name)) {
-      console.warn(`Cannot make notify property '${name}' non-notify.`);
-    }
-    // always add observer
-    if (info.observer) {
-      proto._createPropertyObserver(name, info.observer, allProps[info.observer]);
-    }
-    // always create the mapping from attribute back to property for deserialization.
-    proto._addPropertyToAttributeMap(name);
-  }
-
-  /**
-   * Process all style elements in the element template. Styles with the
-   * `include` attribute are processed such that any styles in
-   * the associated "style modules" are included in the element template.
-   * @param {PolymerElementConstructor} klass Element class
-   * @param {!HTMLTemplateElement} template Template to process
-   * @param {string} is Name of element
-   * @param {string} baseURI Base URI for element
-   * @private
-   */
-  function processElementStyles(klass, template, is, baseURI) {
-    if (!builtCSS) {
-      const templateStyles = template.content.querySelectorAll('style');
-      const stylesWithImports = stylesFromTemplate(template);
-      // insert styles from <link rel="import" type="css"> at the top of the template
-      const linkedStyles = stylesFromModuleImports(is);
-      const firstTemplateChild = template.content.firstElementChild;
-      for (let idx = 0; idx < linkedStyles.length; idx++) {
-        let s = linkedStyles[idx];
-        s.textContent = klass._processStyleText(s.textContent, baseURI);
-        template.content.insertBefore(s, firstTemplateChild);
-      }
-      // keep track of the last "concrete" style in the template we have encountered
-      let templateStyleIndex = 0;
-      // ensure all gathered styles are actually in this template.
-      for (let i = 0; i < stylesWithImports.length; i++) {
-        let s = stylesWithImports[i];
-        let templateStyle = templateStyles[templateStyleIndex];
-        // if the style is not in this template, it's been "included" and
-        // we put a clone of it in the template before the style that included it
-        if (templateStyle !== s) {
-          s = s.cloneNode(true);
-          templateStyle.parentNode.insertBefore(s, templateStyle);
-        } else {
-          templateStyleIndex++;
-        }
-        s.textContent = klass._processStyleText(s.textContent, baseURI);
-      }
-    }
-    if (window.ShadyCSS) {
-      window.ShadyCSS.prepareTemplate(template, is);
-    }
-  }
-
-  /**
-   * Look up template from dom-module for element
-   *
-   * @param {string} is Element name to look up
-   * @return {?HTMLTemplateElement|undefined} Template found in dom module, or
-   *   undefined if not found
-   * @protected
-   */
-  function getTemplateFromDomModule(is) {
-    let template = null;
-    // Under strictTemplatePolicy in 3.x+, dom-module lookup is only allowed
-    // when opted-in via allowTemplateFromDomModule
-    if (is && (!strictTemplatePolicy || allowTemplateFromDomModule)) {
-      template = /** @type {?HTMLTemplateElement} */ (
-          DomModule.import(is, 'template'));
-      // Under strictTemplatePolicy, require any element with an `is`
-      // specified to have a dom-module
-      if (strictTemplatePolicy && !template) {
-        throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${is}`);
-      }
-    }
-    return template;
-  }
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @unrestricted
-   * @implements {Polymer_ElementMixin}
-   * @extends {polymerElementBase}
-   */
-  class PolymerElement extends polymerElementBase {
-
-    /**
-     * Current Polymer version in Semver notation.
-     * @type {string} Semver notation of the current version of Polymer.
-     */
-    static get polymerElementVersion() {
-      return version;
-    }
-
-    /**
-     * Override of PropertiesMixin _finalizeClass to create observers and
-     * find the template.
-     * @return {void}
-     * @protected
-     * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
-     */
-    static _finalizeClass() {
-      super._finalizeClass();
-      const observers = ownObservers(this);
-      if (observers) {
-        this.createObservers(observers, this._properties);
-      }
-      this._prepareTemplate();
-    }
-
-    static _prepareTemplate() {
-      // note: create "working" template that is finalized at instance time
-      let template = /** @type {PolymerElementConstructor} */ (this).template;
-      if (template) {
-        if (typeof template === 'string') {
-          console.error('template getter must return HTMLTemplateElement');
-          template = null;
-        } else if (!legacyOptimizations) {
-          template = template.cloneNode(true);
-        }
-      }
-
-      this.prototype._template = template;
-    }
-
-    /**
-     * Override of PropertiesChanged createProperties to create accessors
-     * and property effects for all of the properties.
-     * @param {!Object} props .
-     * @return {void}
-     * @protected
-     */
-    static createProperties(props) {
-      for (let p in props) {
-        createPropertyFromConfig(this.prototype, p, props[p], props);
-      }
-    }
-
-    /**
-     * Creates observers for the given `observers` array.
-     * Leverages `PropertyEffects` to create observers.
-     * @param {Object} observers Array of observer descriptors for
-     *   this class
-     * @param {Object} dynamicFns Object containing keys for any properties
-     *   that are functions and should trigger the effect when the function
-     *   reference is changed
-     * @return {void}
-     * @protected
-     */
-    static createObservers(observers, dynamicFns) {
-      const proto = this.prototype;
-      for (let i=0; i < observers.length; i++) {
-        proto._createMethodObserver(observers[i], dynamicFns);
-      }
-    }
-
-    /**
-     * Returns the template that will be stamped into this element's shadow root.
-     *
-     * If a `static get is()` getter is defined, the default implementation
-     * will return the first `<template>` in a `dom-module` whose `id`
-     * matches this element's `is`.
-     *
-     * Users may override this getter to return an arbitrary template
-     * (in which case the `is` getter is unnecessary). The template returned
-     * must be an `HTMLTemplateElement`.
-     *
-     * Note that when subclassing, if the super class overrode the default
-     * implementation and the subclass would like to provide an alternate
-     * template via a `dom-module`, it should override this getter and
-     * return `DomModule.import(this.is, 'template')`.
-     *
-     * If a subclass would like to modify the super class template, it should
-     * clone it rather than modify it in place.  If the getter does expensive
-     * work such as cloning/modifying a template, it should memoize the
-     * template for maximum performance:
-     *
-     *   let memoizedTemplate;
-     *   class MySubClass extends MySuperClass {
-     *     static get template() {
-     *       if (!memoizedTemplate) {
-     *         memoizedTemplate = super.template.cloneNode(true);
-     *         let subContent = document.createElement('div');
-     *         subContent.textContent = 'This came from MySubClass';
-     *         memoizedTemplate.content.appendChild(subContent);
-     *       }
-     *       return memoizedTemplate;
-     *     }
-     *   }
-     *
-     * @return {!HTMLTemplateElement|string} Template to be stamped
-     */
-    static get template() {
-      // Explanation of template-related properties:
-      // - constructor.template (this getter): the template for the class.
-      //     This can come from the prototype (for legacy elements), from a
-      //     dom-module, or from the super class's template (or can be overridden
-      //     altogether by the user)
-      // - constructor._template: memoized version of constructor.template
-      // - prototype._template: working template for the element, which will be
-      //     parsed and modified in place. It is a cloned version of
-      //     constructor.template, saved in _finalizeClass(). Note that before
-      //     this getter is called, for legacy elements this could be from a
-      //     _template field on the info object passed to Polymer(), a behavior,
-      //     or set in registered(); once the static getter runs, a clone of it
-      //     will overwrite it on the prototype as the working template.
-      if (!this.hasOwnProperty(JSCompiler_renameProperty('_template', this))) {
-        this._template =
-          // If user has put template on prototype (e.g. in legacy via registered
-          // callback or info object), prefer that first
-          this.prototype.hasOwnProperty(JSCompiler_renameProperty('_template', this.prototype)) ?
-          this.prototype._template :
-          // Look in dom-module associated with this element's is
-          (getTemplateFromDomModule(/** @type {PolymerElementConstructor}*/ (this).is) ||
-          // Next look for superclass template (call the super impl this
-          // way so that `this` points to the superclass)
-          Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.template);
-      }
-      return this._template;
-    }
-
-    /**
-     * Set the template.
-     *
-     * @param {!HTMLTemplateElement|string} value Template to set.
-     */
-    static set template(value) {
-      this._template = value;
-    }
-
-    /**
-     * Path matching the url from which the element was imported.
-     *
-     * This path is used to resolve url's in template style cssText.
-     * The `importPath` property is also set on element instances and can be
-     * used to create bindings relative to the import path.
-     *
-     * For elements defined in ES modules, users should implement
-     * `static get importMeta() { return import.meta; }`, and the default
-     * implementation of `importPath` will  return `import.meta.url`'s path.
-     * For elements defined in HTML imports, this getter will return the path
-     * to the document containing a `dom-module` element matching this
-     * element's static `is` property.
-     *
-     * Note, this path should contain a trailing `/`.
-     *
-     * @return {string} The import path for this element class
-     * @suppress {missingProperties}
-     */
-    static get importPath() {
-      if (!this.hasOwnProperty(JSCompiler_renameProperty('_importPath', this))) {
-        const meta = this.importMeta;
-        if (meta) {
-          this._importPath = pathFromUrl(meta.url);
-        } else {
-          const module = DomModule.import(/** @type {PolymerElementConstructor} */ (this).is);
-          this._importPath = (module && module.assetpath) ||
-            Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.importPath;
-        }
-      }
-      return this._importPath;
-    }
-
-    constructor() {
-      super();
-      /** @type {HTMLTemplateElement} */
-      this._template;
-      /** @type {string} */
-      this._importPath;
-      /** @type {string} */
-      this.rootPath;
-      /** @type {string} */
-      this.importPath;
-      /** @type {StampedTemplate | HTMLElement | ShadowRoot} */
-      this.root;
-      /** @type {!Object<string, !Element>} */
-      this.$;
-    }
-
-    /**
-     * Overrides the default `PropertyAccessors` to ensure class
-     * metaprogramming related to property accessors and effects has
-     * completed (calls `finalize`).
-     *
-     * It also initializes any property defaults provided via `value` in
-     * `properties` metadata.
-     *
-     * @return {void}
-     * @override
-     * @suppress {invalidCasts,missingProperties} go/missingfnprops
-     */
-    _initializeProperties() {
-      this.constructor.finalize();
-      // note: finalize template when we have access to `localName` to
-      // avoid dependence on `is` for polyfilling styling.
-      this.constructor._finalizeTemplate(/** @type {!HTMLElement} */(this).localName);
-      super._initializeProperties();
-      // set path defaults
-      this.rootPath = rootPath;
-      this.importPath = this.constructor.importPath;
-      // apply property defaults...
-      let p$ = propertyDefaults(this.constructor);
-      if (!p$) {
-        return;
-      }
-      for (let p in p$) {
-        let info = p$[p];
-        // Don't set default value if there is already an own property, which
-        // happens when a `properties` property with default but no effects had
-        // a property set (e.g. bound) by its host before upgrade
-        if (!this.hasOwnProperty(p)) {
-          let value = typeof info.value == 'function' ?
-            info.value.call(this) :
-            info.value;
-          // Set via `_setProperty` if there is an accessor, to enable
-          // initializing readOnly property defaults
-          if (this._hasAccessor(p)) {
-            this._setPendingProperty(p, value, true);
-          } else {
-            this[p] = value;
-          }
-        }
-      }
-    }
-
-    /**
-     * Gather style text for a style element in the template.
-     *
-     * @param {string} cssText Text containing styling to process
-     * @param {string} baseURI Base URI to rebase CSS paths against
-     * @return {string} The processed CSS text
-     * @protected
-     */
-    static _processStyleText(cssText, baseURI) {
-      return resolveCss(cssText, baseURI);
-    }
-
-    /**
-    * Configures an element `proto` to function with a given `template`.
-    * The element name `is` and extends `ext` must be specified for ShadyCSS
-    * style scoping.
-    *
-    * @param {string} is Tag name (or type extension name) for this element
-    * @return {void}
-    * @protected
-    */
-    static _finalizeTemplate(is) {
-      /** @const {HTMLTemplateElement} */
-      const template = this.prototype._template;
-      if (template && !template.__polymerFinalized) {
-        template.__polymerFinalized = true;
-        const importPath = this.importPath;
-        const baseURI = importPath ? resolveUrl(importPath) : '';
-        // e.g. support `include="module-name"`, and ShadyCSS
-        processElementStyles(this, template, is, baseURI);
-        this.prototype._bindTemplate(template);
-      }
-    }
-
-    /**
-     * Provides a default implementation of the standard Custom Elements
-     * `connectedCallback`.
-     *
-     * The default implementation enables the property effects system and
-     * flushes any pending properties, and updates shimmed CSS properties
-     * when using the ShadyCSS scoping/custom properties polyfill.
-     *
-     * @override
-     * @suppress {missingProperties, invalidCasts} Super may or may not
-     *     implement the callback
-     * @return {void}
-     */
-    connectedCallback() {
-      if (window.ShadyCSS && this._template) {
-        window.ShadyCSS.styleElement(/** @type {!HTMLElement} */(this));
-      }
-      super.connectedCallback();
-    }
-
-    /**
-     * Stamps the element template.
-     *
-     * @return {void}
-     * @override
-     */
-    ready() {
-      if (this._template) {
-        this.root = this._stampTemplate(this._template);
-        this.$ = this.root.$;
-      }
-      super.ready();
-    }
-
-    /**
-     * Implements `PropertyEffects`'s `_readyClients` call. Attaches
-     * element dom by calling `_attachDom` with the dom stamped from the
-     * element's template via `_stampTemplate`. Note that this allows
-     * client dom to be attached to the element prior to any observers
-     * running.
-     *
-     * @return {void}
-     * @override
-     */
-    _readyClients() {
-      if (this._template) {
-        this.root = this._attachDom(/** @type {StampedTemplate} */(this.root));
-      }
-      // The super._readyClients here sets the clients initialized flag.
-      // We must wait to do this until after client dom is created/attached
-      // so that this flag can be checked to prevent notifications fired
-      // during this process from being handled before clients are ready.
-      super._readyClients();
-    }
-
-
-    /**
-     * Attaches an element's stamped dom to itself. By default,
-     * this method creates a `shadowRoot` and adds the dom to it.
-     * However, this method may be overridden to allow an element
-     * to put its dom in another location.
-     *
-     * @override
-     * @throws {Error}
-     * @suppress {missingReturn}
-     * @param {StampedTemplate} dom to attach to the element.
-     * @return {ShadowRoot} node to which the dom has been attached.
-     */
-    _attachDom(dom) {
-      const n = wrap(this);
-      if (n.attachShadow) {
-        if (dom) {
-          if (!n.shadowRoot) {
-            n.attachShadow({mode: 'open'});
-          }
-          n.shadowRoot.appendChild(dom);
-          if (syncInitialRender && window.ShadyDOM) {
-            ShadyDOM.flushInitial(n.shadowRoot);
-          }
-          return n.shadowRoot;
-        }
-        return null;
-      } else {
-        throw new Error('ShadowDOM not available. ' +
-          // TODO(sorvell): move to compile-time conditional when supported
-        'PolymerElement can create dom as children instead of in ' +
-        'ShadowDOM by setting `this.root = this;\` before \`ready\`.');
-      }
-    }
-
-    /**
-     * When using the ShadyCSS scoping and custom property shim, causes all
-     * shimmed styles in this element (and its subtree) to be updated
-     * based on current custom property values.
-     *
-     * The optional parameter overrides inline custom property styles with an
-     * object of properties where the keys are CSS properties, and the values
-     * are strings.
-     *
-     * Example: `this.updateStyles({'--color': 'blue'})`
-     *
-     * These properties are retained unless a value of `null` is set.
-     *
-     * Note: This function does not support updating CSS mixins.
-     * You can not dynamically change the value of an `@apply`.
-     *
-     * @override
-     * @param {Object=} properties Bag of custom property key/values to
-     *   apply to this element.
-     * @return {void}
-     * @suppress {invalidCasts}
-     */
-    updateStyles(properties) {
-      if (window.ShadyCSS) {
-        window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */(this), properties);
-      }
-    }
-
-    /**
-     * Rewrites a given URL relative to a base URL. The base URL defaults to
-     * the original location of the document containing the `dom-module` for
-     * this element. This method will return the same URL before and after
-     * bundling.
-     *
-     * Note that this function performs no resolution for URLs that start
-     * with `/` (absolute URLs) or `#` (hash identifiers).  For general purpose
-     * URL resolution, use `window.URL`.
-     *
-     * @override
-     * @param {string} url URL to resolve.
-     * @param {string=} base Optional base URL to resolve against, defaults
-     * to the element's `importPath`
-     * @return {string} Rewritten URL relative to base
-     */
-    resolveUrl(url, base) {
-      if (!base && this.importPath) {
-        base = resolveUrl(this.importPath);
-      }
-      return resolveUrl(url, base);
-    }
-
-    /**
-     * Overrides `PropertyEffects` to add map of dynamic functions on
-     * template info, for consumption by `PropertyEffects` template binding
-     * code. This map determines which method templates should have accessors
-     * created for them.
-     *
-     * @param {!HTMLTemplateElement} template Template
-     * @param {!TemplateInfo} templateInfo Template metadata for current template
-     * @param {!NodeInfo} nodeInfo Node metadata for current template.
-     * @return {boolean} .
-     * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
-     */
-    static _parseTemplateContent(template, templateInfo, nodeInfo) {
-      templateInfo.dynamicFns = templateInfo.dynamicFns || this._properties;
-      return super._parseTemplateContent(template, templateInfo, nodeInfo);
-    }
-
-    /**
-     * Overrides `PropertyEffects` to warn on use of undeclared properties in
-     * template.
-     *
-     * @param {Object} templateInfo Template metadata to add effect to
-     * @param {string} prop Property that should trigger the effect
-     * @param {Object=} effect Effect metadata object
-     * @return {void}
-     * @protected
-     * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
-     */
-    static _addTemplatePropertyEffect(templateInfo, prop, effect) {
-      // Warn if properties are used in template without being declared.
-      // Properties must be listed in `properties` to be included in
-      // `observedAttributes` since CE V1 reads that at registration time, and
-      // since we want to keep template parsing lazy, we can't automatically
-      // add undeclared properties used in templates to `observedAttributes`.
-      // The warning is only enabled in `legacyOptimizations` mode, since
-      // we don't want to spam existing users who might have adopted the
-      // shorthand when attribute deserialization is not important.
-      if (legacyOptimizations && !(prop in this._properties)) {
-        console.warn(`Property '${prop}' used in template but not declared in 'properties'; ` +
-          `attribute will not be observed.`);
-      }
-      return super._addTemplatePropertyEffect(templateInfo, prop, effect);
-    }
-
-  }
-
-  return PolymerElement;
-});
-
-/**
- * When using the ShadyCSS scoping and custom property shim, causes all
- * shimmed `styles` (via `custom-style`) in the document (and its subtree)
- * to be updated based on current custom property values.
- *
- * The optional parameter overrides inline custom property styles with an
- * object of properties where the keys are CSS properties, and the values
- * are strings.
- *
- * Example: `updateStyles({'--color': 'blue'})`
- *
- * These properties are retained unless a value of `null` is set.
- *
- * @param {Object=} props Bag of custom property key/values to
- *   apply to the document.
- * @return {void}
- */
-export const updateStyles = function(props) {
-  if (window.ShadyCSS) {
-    window.ShadyCSS.styleDocument(props);
-  }
-};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/gesture-event-listeners.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/gesture-event-listeners.js
deleted file mode 100644
index 04ad6ef..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/gesture-event-listeners.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-
-import { dedupingMixin } from '../utils/mixin.js';
-import { addListener, removeListener } from '../utils/gestures.js';
-
-/**
- * Element class mixin that provides API for adding Polymer's cross-platform
- * gesture events to nodes.
- *
- * The API is designed to be compatible with override points implemented
- * in `TemplateStamp` such that declarative event listeners in
- * templates will support gesture events when this mixin is applied along with
- * `TemplateStamp`.
- *
- * @mixinFunction
- * @polymer
- * @summary Element class mixin that provides API for adding Polymer's
- *   cross-platform
- * gesture events to nodes
- */
-export const GestureEventListeners = dedupingMixin(
-    /**
-     * @template T
-     * @param {function(new:T)} superClass Class to apply mixin to.
-     * @return {function(new:T)} superClass with mixin applied.
-     */
-    (superClass) => {
-      /**
-       * @polymer
-       * @mixinClass
-       * @implements {Polymer_GestureEventListeners}
-       */
-      class GestureEventListeners extends superClass {
-        /**
-         * Add the event listener to the node if it is a gestures event.
-         *
-         * @param {!EventTarget} node Node to add event listener to
-         * @param {string} eventName Name of event
-         * @param {function(!Event):void} handler Listener function to add
-         * @return {void}
-         * @override
-         */
-        _addEventListenerToNode(node, eventName, handler) {
-          if (!addListener(node, eventName, handler)) {
-            super._addEventListenerToNode(node, eventName, handler);
-          }
-        }
-
-        /**
-         * Remove the event listener to the node if it is a gestures event.
-         *
-         * @param {!EventTarget} node Node to remove event listener from
-         * @param {string} eventName Name of event
-         * @param {function(!Event):void} handler Listener function to remove
-         * @return {void}
-         * @override
-         */
-        _removeEventListenerFromNode(node, eventName, handler) {
-          if (!removeListener(node, eventName, handler)) {
-            super._removeEventListenerFromNode(node, eventName, handler);
-          }
-        }
-      }
-
-      return GestureEventListeners;
-    });
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/mutable-data.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/mutable-data.js
deleted file mode 100644
index 3c937b94..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/mutable-data.js
+++ /dev/null
@@ -1,194 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import { dedupingMixin } from '../utils/mixin.js';
-
-// Common implementation for mixin & behavior
-function mutablePropertyChange(inst, property, value, old, mutableData) {
-  let isObject;
-  if (mutableData) {
-    isObject = (typeof value === 'object' && value !== null);
-    // Pull `old` for Objects from temp cache, but treat `null` as a primitive
-    if (isObject) {
-      old = inst.__dataTemp[property];
-    }
-  }
-  // Strict equality check, but return false for NaN===NaN
-  let shouldChange = (old !== value && (old === old || value === value));
-  // Objects are stored in temporary cache (cleared at end of
-  // turn), which is used for dirty-checking
-  if (isObject && shouldChange) {
-    inst.__dataTemp[property] = value;
-  }
-  return shouldChange;
-}
-
-/**
- * Element class mixin to skip strict dirty-checking for objects and arrays
- * (always consider them to be "dirty"), for use on elements utilizing
- * `PropertyEffects`
- *
- * By default, `PropertyEffects` performs strict dirty checking on
- * objects, which means that any deep modifications to an object or array will
- * not be propagated unless "immutable" data patterns are used (i.e. all object
- * references from the root to the mutation were changed).
- *
- * Polymer also provides a proprietary data mutation and path notification API
- * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
- * mutation and notification of deep changes in an object graph to all elements
- * bound to the same object graph.
- *
- * In cases where neither immutable patterns nor the data mutation API can be
- * used, applying this mixin will cause Polymer to skip dirty checking for
- * objects and arrays (always consider them to be "dirty").  This allows a
- * user to make a deep modification to a bound object graph, and then either
- * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
- * (e.g. `this.notifyPath('items')`) to update the tree.  Note that all
- * elements that wish to be updated based on deep mutations must apply this
- * mixin or otherwise skip strict dirty checking for objects/arrays.
- * Specifically, any elements in the binding tree between the source of a
- * mutation and the consumption of it must apply this mixin or enable the
- * `OptionalMutableData` mixin.
- *
- * In order to make the dirty check strategy configurable, see
- * `OptionalMutableData`.
- *
- * Note, the performance characteristics of propagating large object graphs
- * will be worse as opposed to using strict dirty checking with immutable
- * patterns or Polymer's path notification API.
- *
- * @mixinFunction
- * @polymer
- * @summary Element class mixin to skip strict dirty-checking for objects
- *   and arrays
- */
-export const MutableData = dedupingMixin(superClass => {
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_MutableData}
-   */
-  class MutableData extends superClass {
-    /**
-     * Overrides `PropertyEffects` to provide option for skipping
-     * strict equality checking for Objects and Arrays.
-     *
-     * This method pulls the value to dirty check against from the `__dataTemp`
-     * cache (rather than the normal `__data` cache) for Objects.  Since the temp
-     * cache is cleared at the end of a turn, this implementation allows
-     * side-effects of deep object changes to be processed by re-setting the
-     * same object (using the temp cache as an in-turn backstop to prevent
-     * cycles due to 2-way notification).
-     *
-     * @param {string} property Property name
-     * @param {*} value New property value
-     * @param {*} old Previous property value
-     * @return {boolean} Whether the property should be considered a change
-     * @protected
-     */
-    _shouldPropertyChange(property, value, old) {
-      return mutablePropertyChange(this, property, value, old, true);
-    }
-
-  }
-
-  return MutableData;
-
-});
-
-/**
- * Element class mixin to add the optional ability to skip strict
- * dirty-checking for objects and arrays (always consider them to be
- * "dirty") by setting a `mutable-data` attribute on an element instance.
- *
- * By default, `PropertyEffects` performs strict dirty checking on
- * objects, which means that any deep modifications to an object or array will
- * not be propagated unless "immutable" data patterns are used (i.e. all object
- * references from the root to the mutation were changed).
- *
- * Polymer also provides a proprietary data mutation and path notification API
- * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
- * mutation and notification of deep changes in an object graph to all elements
- * bound to the same object graph.
- *
- * In cases where neither immutable patterns nor the data mutation API can be
- * used, applying this mixin will allow Polymer to skip dirty checking for
- * objects and arrays (always consider them to be "dirty").  This allows a
- * user to make a deep modification to a bound object graph, and then either
- * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
- * (e.g. `this.notifyPath('items')`) to update the tree.  Note that all
- * elements that wish to be updated based on deep mutations must apply this
- * mixin or otherwise skip strict dirty checking for objects/arrays.
- * Specifically, any elements in the binding tree between the source of a
- * mutation and the consumption of it must enable this mixin or apply the
- * `MutableData` mixin.
- *
- * While this mixin adds the ability to forgo Object/Array dirty checking,
- * the `mutableData` flag defaults to false and must be set on the instance.
- *
- * Note, the performance characteristics of propagating large object graphs
- * will be worse by relying on `mutableData: true` as opposed to using
- * strict dirty checking with immutable patterns or Polymer's path notification
- * API.
- *
- * @mixinFunction
- * @polymer
- * @summary Element class mixin to optionally skip strict dirty-checking
- *   for objects and arrays
- */
-export const OptionalMutableData = dedupingMixin(superClass => {
-
-  /**
-   * @mixinClass
-   * @polymer
-   * @implements {Polymer_OptionalMutableData}
-   */
-  class OptionalMutableData extends superClass {
-
-    static get properties() {
-      return {
-        /**
-         * Instance-level flag for configuring the dirty-checking strategy
-         * for this element.  When true, Objects and Arrays will skip dirty
-         * checking, otherwise strict equality checking will be used.
-         */
-        mutableData: Boolean
-      };
-    }
-
-    /**
-     * Overrides `PropertyEffects` to provide option for skipping
-     * strict equality checking for Objects and Arrays.
-     *
-     * When `this.mutableData` is true on this instance, this method
-     * pulls the value to dirty check against from the `__dataTemp` cache
-     * (rather than the normal `__data` cache) for Objects.  Since the temp
-     * cache is cleared at the end of a turn, this implementation allows
-     * side-effects of deep object changes to be processed by re-setting the
-     * same object (using the temp cache as an in-turn backstop to prevent
-     * cycles due to 2-way notification).
-     *
-     * @param {string} property Property name
-     * @param {*} value New property value
-     * @param {*} old Previous property value
-     * @return {boolean} Whether the property should be considered a change
-     * @protected
-     */
-    _shouldPropertyChange(property, value, old) {
-      return mutablePropertyChange(this, property, value, old, this.mutableData);
-    }
-  }
-
-  return OptionalMutableData;
-
-});
-
-// Export for use by legacy behavior
-MutableData._mutablePropertyChange = mutablePropertyChange;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-changed.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-changed.js
deleted file mode 100644
index 78ec739..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-changed.js
+++ /dev/null
@@ -1,555 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-
-import { dedupingMixin } from '../utils/mixin.js';
-import { microTask } from '../utils/async.js';
-import { wrap } from '../utils/wrap.js';
-
-/** @const {!AsyncInterface} */
-const microtask = microTask;
-
-/**
- * Element class mixin that provides basic meta-programming for creating one
- * or more property accessors (getter/setter pair) that enqueue an async
- * (batched) `_propertiesChanged` callback.
- *
- * For basic usage of this mixin, call `MyClass.createProperties(props)`
- * once at class definition time to create property accessors for properties
- * named in props, implement `_propertiesChanged` to react as desired to
- * property changes, and implement `static get observedAttributes()` and
- * include lowercase versions of any property names that should be set from
- * attributes. Last, call `this._enableProperties()` in the element's
- * `connectedCallback` to enable the accessors.
- *
- * @mixinFunction
- * @polymer
- * @summary Element class mixin for reacting to property changes from
- *   generated property accessors.
- */
-export const PropertiesChanged = dedupingMixin(
-    /**
-     * @template T
-     * @param {function(new:T)} superClass Class to apply mixin to.
-     * @return {function(new:T)} superClass with mixin applied.
-     */
-    (superClass) => {
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_PropertiesChanged}
-   * @unrestricted
-   */
-  class PropertiesChanged extends superClass {
-
-    /**
-     * Creates property accessors for the given property names.
-     * @param {!Object} props Object whose keys are names of accessors.
-     * @return {void}
-     * @protected
-     */
-    static createProperties(props) {
-      const proto = this.prototype;
-      for (let prop in props) {
-        // don't stomp an existing accessor
-        if (!(prop in proto)) {
-          proto._createPropertyAccessor(prop);
-        }
-      }
-    }
-
-    /**
-     * Returns an attribute name that corresponds to the given property.
-     * The attribute name is the lowercased property name. Override to
-     * customize this mapping.
-     * @param {string} property Property to convert
-     * @return {string} Attribute name corresponding to the given property.
-     *
-     * @protected
-     */
-    static attributeNameForProperty(property) {
-      return property.toLowerCase();
-    }
-
-    /**
-     * Override point to provide a type to which to deserialize a value to
-     * a given property.
-     * @param {string} name Name of property
-     *
-     * @protected
-     */
-    static typeForProperty(name) { } //eslint-disable-line no-unused-vars
-
-    /**
-     * Creates a setter/getter pair for the named property with its own
-     * local storage.  The getter returns the value in the local storage,
-     * and the setter calls `_setProperty`, which updates the local storage
-     * for the property and enqueues a `_propertiesChanged` callback.
-     *
-     * This method may be called on a prototype or an instance.  Calling
-     * this method may overwrite a property value that already exists on
-     * the prototype/instance by creating the accessor.
-     *
-     * @param {string} property Name of the property
-     * @param {boolean=} readOnly When true, no setter is created; the
-     *   protected `_setProperty` function must be used to set the property
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _createPropertyAccessor(property, readOnly) {
-      this._addPropertyToAttributeMap(property);
-      if (!this.hasOwnProperty('__dataHasAccessor')) {
-        this.__dataHasAccessor = Object.assign({}, this.__dataHasAccessor);
-      }
-      if (!this.__dataHasAccessor[property]) {
-        this.__dataHasAccessor[property] = true;
-        this._definePropertyAccessor(property, readOnly);
-      }
-    }
-
-    /**
-     * Adds the given `property` to a map matching attribute names
-     * to property names, using `attributeNameForProperty`. This map is
-     * used when deserializing attribute values to properties.
-     *
-     * @param {string} property Name of the property
-     * @override
-     */
-    _addPropertyToAttributeMap(property) {
-      if (!this.hasOwnProperty('__dataAttributes')) {
-        this.__dataAttributes = Object.assign({}, this.__dataAttributes);
-      }
-      if (!this.__dataAttributes[property]) {
-        const attr = this.constructor.attributeNameForProperty(property);
-        this.__dataAttributes[attr] = property;
-      }
-    }
-
-    /**
-     * Defines a property accessor for the given property.
-     * @param {string} property Name of the property
-     * @param {boolean=} readOnly When true, no setter is created
-     * @return {void}
-     * @override
-     */
-     _definePropertyAccessor(property, readOnly) {
-      Object.defineProperty(this, property, {
-        /* eslint-disable valid-jsdoc */
-        /** @this {PropertiesChanged} */
-        get() {
-          return this._getProperty(property);
-        },
-        /** @this {PropertiesChanged} */
-        set: readOnly ? function () {} : function (value) {
-          this._setProperty(property, value);
-        }
-        /* eslint-enable */
-      });
-    }
-
-    constructor() {
-      super();
-      /** @protected {boolean} */
-      this.__dataEnabled = false;
-      this.__dataReady = false;
-      this.__dataInvalid = false;
-      this.__data = {};
-      this.__dataPending = null;
-      this.__dataOld = null;
-      this.__dataInstanceProps = null;
-      this.__serializing = false;
-      this._initializeProperties();
-    }
-
-    /**
-     * Lifecycle callback called when properties are enabled via
-     * `_enableProperties`.
-     *
-     * Users may override this function to implement behavior that is
-     * dependent on the element having its property data initialized, e.g.
-     * from defaults (initialized from `constructor`, `_initializeProperties`),
-     * `attributeChangedCallback`, or values propagated from host e.g. via
-     * bindings.  `super.ready()` must be called to ensure the data system
-     * becomes enabled.
-     *
-     * @return {void}
-     * @public
-     * @override
-     */
-    ready() {
-      this.__dataReady = true;
-      this._flushProperties();
-    }
-
-    /**
-     * Initializes the local storage for property accessors.
-     *
-     * Provided as an override point for performing any setup work prior
-     * to initializing the property accessor system.
-     *
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _initializeProperties() {
-      // Capture instance properties; these will be set into accessors
-      // during first flush. Don't set them here, since we want
-      // these to overwrite defaults/constructor assignments
-      for (let p in this.__dataHasAccessor) {
-        if (this.hasOwnProperty(p)) {
-          this.__dataInstanceProps = this.__dataInstanceProps || {};
-          this.__dataInstanceProps[p] = this[p];
-          delete this[p];
-        }
-      }
-    }
-
-    /**
-     * Called at ready time with bag of instance properties that overwrote
-     * accessors when the element upgraded.
-     *
-     * The default implementation sets these properties back into the
-     * setter at ready time.  This method is provided as an override
-     * point for customizing or providing more efficient initialization.
-     *
-     * @param {Object} props Bag of property values that were overwritten
-     *   when creating property accessors.
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _initializeInstanceProperties(props) {
-      Object.assign(this, props);
-    }
-
-    /**
-     * Updates the local storage for a property (via `_setPendingProperty`)
-     * and enqueues a `_proeprtiesChanged` callback.
-     *
-     * @param {string} property Name of the property
-     * @param {*} value Value to set
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _setProperty(property, value) {
-      if (this._setPendingProperty(property, value)) {
-        this._invalidateProperties();
-      }
-    }
-
-    /**
-     * Returns the value for the given property.
-     * @param {string} property Name of property
-     * @return {*} Value for the given property
-     * @protected
-     * @override
-     */
-    _getProperty(property) {
-      return this.__data[property];
-    }
-
-    /* eslint-disable no-unused-vars */
-    /**
-     * Updates the local storage for a property, records the previous value,
-     * and adds it to the set of "pending changes" that will be passed to the
-     * `_propertiesChanged` callback.  This method does not enqueue the
-     * `_propertiesChanged` callback.
-     *
-     * @param {string} property Name of the property
-     * @param {*} value Value to set
-     * @param {boolean=} ext Not used here; affordance for closure
-     * @return {boolean} Returns true if the property changed
-     * @protected
-     * @override
-     */
-    _setPendingProperty(property, value, ext) {
-      let old = this.__data[property];
-      let changed = this._shouldPropertyChange(property, value, old);
-      if (changed) {
-        if (!this.__dataPending) {
-          this.__dataPending = {};
-          this.__dataOld = {};
-        }
-        // Ensure old is captured from the last turn
-        if (this.__dataOld && !(property in this.__dataOld)) {
-          this.__dataOld[property] = old;
-        }
-        this.__data[property] = value;
-        this.__dataPending[property] = value;
-      }
-      return changed;
-    }
-    /* eslint-enable */
-
-    /**
-     * Marks the properties as invalid, and enqueues an async
-     * `_propertiesChanged` callback.
-     *
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _invalidateProperties() {
-      if (!this.__dataInvalid && this.__dataReady) {
-        this.__dataInvalid = true;
-        microtask.run(() => {
-          if (this.__dataInvalid) {
-            this.__dataInvalid = false;
-            this._flushProperties();
-          }
-        });
-      }
-    }
-
-    /**
-     * Call to enable property accessor processing. Before this method is
-     * called accessor values will be set but side effects are
-     * queued. When called, any pending side effects occur immediately.
-     * For elements, generally `connectedCallback` is a normal spot to do so.
-     * It is safe to call this method multiple times as it only turns on
-     * property accessors once.
-     *
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _enableProperties() {
-      if (!this.__dataEnabled) {
-        this.__dataEnabled = true;
-        if (this.__dataInstanceProps) {
-          this._initializeInstanceProperties(this.__dataInstanceProps);
-          this.__dataInstanceProps = null;
-        }
-        this.ready();
-      }
-    }
-
-    /**
-     * Calls the `_propertiesChanged` callback with the current set of
-     * pending changes (and old values recorded when pending changes were
-     * set), and resets the pending set of changes. Generally, this method
-     * should not be called in user code.
-     *
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _flushProperties() {
-      const props = this.__data;
-      const changedProps = this.__dataPending;
-      const old = this.__dataOld;
-      if (this._shouldPropertiesChange(props, changedProps, old)) {
-        this.__dataPending = null;
-        this.__dataOld = null;
-        this._propertiesChanged(props, changedProps, old);
-      }
-    }
-
-    /**
-     * Called in `_flushProperties` to determine if `_propertiesChanged`
-     * should be called. The default implementation returns true if
-     * properties are pending. Override to customize when
-     * `_propertiesChanged` is called.
-     * @param {!Object} currentProps Bag of all current accessor values
-     * @param {?Object} changedProps Bag of properties changed since the last
-     *   call to `_propertiesChanged`
-     * @param {?Object} oldProps Bag of previous values for each property
-     *   in `changedProps`
-     * @return {boolean} true if changedProps is truthy
-     * @override
-     */
-    _shouldPropertiesChange(currentProps, changedProps, oldProps) { // eslint-disable-line no-unused-vars
-      return Boolean(changedProps);
-    }
-
-    /**
-     * Callback called when any properties with accessors created via
-     * `_createPropertyAccessor` have been set.
-     *
-     * @param {!Object} currentProps Bag of all current accessor values
-     * @param {?Object} changedProps Bag of properties changed since the last
-     *   call to `_propertiesChanged`
-     * @param {?Object} oldProps Bag of previous values for each property
-     *   in `changedProps`
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _propertiesChanged(currentProps, changedProps, oldProps) { // eslint-disable-line no-unused-vars
-    }
-
-    /**
-     * Method called to determine whether a property value should be
-     * considered as a change and cause the `_propertiesChanged` callback
-     * to be enqueued.
-     *
-     * The default implementation returns `true` if a strict equality
-     * check fails. The method always returns false for `NaN`.
-     *
-     * Override this method to e.g. provide stricter checking for
-     * Objects/Arrays when using immutable patterns.
-     *
-     * @param {string} property Property name
-     * @param {*} value New property value
-     * @param {*} old Previous property value
-     * @return {boolean} Whether the property should be considered a change
-     *   and enqueue a `_proeprtiesChanged` callback
-     * @protected
-     * @override
-     */
-    _shouldPropertyChange(property, value, old) {
-      return (
-        // Strict equality check
-        (old !== value &&
-          // This ensures (old==NaN, value==NaN) always returns false
-          (old === old || value === value))
-      );
-    }
-
-    /**
-     * Implements native Custom Elements `attributeChangedCallback` to
-     * set an attribute value to a property via `_attributeToProperty`.
-     *
-     * @param {string} name Name of attribute that changed
-     * @param {?string} old Old attribute value
-     * @param {?string} value New attribute value
-     * @param {?string=} namespace Attribute namespace.
-     * @return {void}
-     * @suppress {missingProperties} Super may or may not implement the callback
-     * @override
-     */
-    attributeChangedCallback(name, old, value, namespace) {
-      if (old !== value) {
-        this._attributeToProperty(name, value);
-      }
-      if (super.attributeChangedCallback) {
-        super.attributeChangedCallback(name, old, value, namespace);
-      }
-    }
-
-    /**
-     * Deserializes an attribute to its associated property.
-     *
-     * This method calls the `_deserializeValue` method to convert the string to
-     * a typed value.
-     *
-     * @param {string} attribute Name of attribute to deserialize.
-     * @param {?string} value of the attribute.
-     * @param {*=} type type to deserialize to, defaults to the value
-     * returned from `typeForProperty`
-     * @return {void}
-     * @override
-     */
-    _attributeToProperty(attribute, value, type) {
-      if (!this.__serializing) {
-        const map = this.__dataAttributes;
-        const property = map && map[attribute] || attribute;
-        this[property] = this._deserializeValue(value, type ||
-          this.constructor.typeForProperty(property));
-      }
-    }
-
-    /**
-     * Serializes a property to its associated attribute.
-     *
-     * @suppress {invalidCasts} Closure can't figure out `this` is an element.
-     *
-     * @param {string} property Property name to reflect.
-     * @param {string=} attribute Attribute name to reflect to.
-     * @param {*=} value Property value to refect.
-     * @return {void}
-     * @override
-     */
-    _propertyToAttribute(property, attribute, value) {
-      this.__serializing = true;
-      value = (arguments.length < 3) ? this[property] : value;
-      this._valueToNodeAttribute(/** @type {!HTMLElement} */(this), value,
-        attribute || this.constructor.attributeNameForProperty(property));
-      this.__serializing = false;
-    }
-
-    /**
-     * Sets a typed value to an HTML attribute on a node.
-     *
-     * This method calls the `_serializeValue` method to convert the typed
-     * value to a string.  If the `_serializeValue` method returns `undefined`,
-     * the attribute will be removed (this is the default for boolean
-     * type `false`).
-     *
-     * @param {Element} node Element to set attribute to.
-     * @param {*} value Value to serialize.
-     * @param {string} attribute Attribute name to serialize to.
-     * @return {void}
-     * @override
-     */
-    _valueToNodeAttribute(node, value, attribute) {
-      const str = this._serializeValue(value);
-      if (str === undefined) {
-        node.removeAttribute(attribute);
-      } else {
-        if (attribute === 'class' || attribute === 'name' || attribute === 'slot') {
-          node = /** @type {?Element} */(wrap(node));
-        }
-        node.setAttribute(attribute, str);
-      }
-    }
-
-    /**
-     * Converts a typed JavaScript value to a string.
-     *
-     * This method is called when setting JS property values to
-     * HTML attributes.  Users may override this method to provide
-     * serialization for custom types.
-     *
-     * @param {*} value Property value to serialize.
-     * @return {string | undefined} String serialized from the provided
-     * property  value.
-     * @override
-     */
-    _serializeValue(value) {
-      switch (typeof value) {
-        case 'boolean':
-          return value ? '' : undefined;
-        default:
-          return value != null ? value.toString() : undefined;
-      }
-    }
-
-    /**
-     * Converts a string to a typed JavaScript value.
-     *
-     * This method is called when reading HTML attribute values to
-     * JS properties.  Users may override this method to provide
-     * deserialization for custom `type`s. Types for `Boolean`, `String`,
-     * and `Number` convert attributes to the expected types.
-     *
-     * @param {?string} value Value to deserialize.
-     * @param {*=} type Type to deserialize the string to.
-     * @return {*} Typed value deserialized from the provided string.
-     * @override
-     */
-    _deserializeValue(value, type) {
-      switch (type) {
-        case Boolean:
-          return (value !== null);
-        case Number:
-          return Number(value);
-        default:
-          return value;
-      }
-    }
-
-  }
-
-  return PropertiesChanged;
-});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-mixin.js
deleted file mode 100644
index 871937b..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-mixin.js
+++ /dev/null
@@ -1,233 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-
-import { dedupingMixin } from '../utils/mixin.js';
-import { register, incrementInstanceCount } from '../utils/telemetry.js';
-import { PropertiesChanged } from './properties-changed.js';
-
-/**
- * Creates a copy of `props` with each property normalized such that
- * upgraded it is an object with at least a type property { type: Type}.
- *
- * @param {Object} props Properties to normalize
- * @return {Object} Copy of input `props` with normalized properties that
- * are in the form {type: Type}
- * @private
- */
-function normalizeProperties(props) {
-  const output = {};
-  for (let p in props) {
-    const o = props[p];
-    output[p] = (typeof o === 'function') ? {type: o} : o;
-  }
-  return output;
-}
-
-/**
- * Mixin that provides a minimal starting point to using the PropertiesChanged
- * mixin by providing a mechanism to declare properties in a static
- * getter (e.g. static get properties() { return { foo: String } }). Changes
- * are reported via the `_propertiesChanged` method.
- *
- * This mixin provides no specific support for rendering. Users are expected
- * to create a ShadowRoot and put content into it and update it in whatever
- * way makes sense. This can be done in reaction to properties changing by
- * implementing `_propertiesChanged`.
- *
- * @mixinFunction
- * @polymer
- * @appliesMixin PropertiesChanged
- * @summary Mixin that provides a minimal starting point for using
- * the PropertiesChanged mixin by providing a declarative `properties` object.
- */
-export const PropertiesMixin = dedupingMixin(superClass => {
-
- /**
-  * @constructor
-  * @implements {Polymer_PropertiesChanged}
-  * @private
-  */
- const base = PropertiesChanged(superClass);
-
- /**
-  * Returns the super class constructor for the given class, if it is an
-  * instance of the PropertiesMixin.
-  *
-  * @param {!PropertiesMixinConstructor} constructor PropertiesMixin constructor
-  * @return {?PropertiesMixinConstructor} Super class constructor
-  */
- function superPropertiesClass(constructor) {
-   const superCtor = Object.getPrototypeOf(constructor);
-
-   // Note, the `PropertiesMixin` class below only refers to the class
-   // generated by this call to the mixin; the instanceof test only works
-   // because the mixin is deduped and guaranteed only to apply once, hence
-   // all constructors in a proto chain will see the same `PropertiesMixin`
-   return (superCtor.prototype instanceof PropertiesMixin) ?
-     /** @type {!PropertiesMixinConstructor} */ (superCtor) : null;
- }
-
- /**
-  * Returns a memoized version of the `properties` object for the
-  * given class. Properties not in object format are converted to at
-  * least {type}.
-  *
-  * @param {PropertiesMixinConstructor} constructor PropertiesMixin constructor
-  * @return {Object} Memoized properties object
-  */
- function ownProperties(constructor) {
-   if (!constructor.hasOwnProperty(JSCompiler_renameProperty('__ownProperties', constructor))) {
-     let props = null;
-
-     if (constructor.hasOwnProperty(JSCompiler_renameProperty('properties', constructor))) {
-       const properties = constructor.properties;
-
-       if (properties) {
-        props = normalizeProperties(properties);
-       }
-     }
-
-     constructor.__ownProperties = props;
-   }
-   return constructor.__ownProperties;
- }
-
- /**
-  * @polymer
-  * @mixinClass
-  * @extends {base}
-  * @implements {Polymer_PropertiesMixin}
-  * @unrestricted
-  */
- class PropertiesMixin extends base {
-
-   /**
-    * Implements standard custom elements getter to observes the attributes
-    * listed in `properties`.
-    * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
-    */
-   static get observedAttributes() {
-     if (!this.hasOwnProperty('__observedAttributes')) {
-       register(this.prototype);
-       const props = this._properties;
-       this.__observedAttributes = props ? Object.keys(props).map(p => this.attributeNameForProperty(p)) : [];
-     }
-     return this.__observedAttributes;
-   }
-
-   /**
-    * Finalizes an element definition, including ensuring any super classes
-    * are also finalized. This includes ensuring property
-    * accessors exist on the element prototype. This method calls
-    * `_finalizeClass` to finalize each constructor in the prototype chain.
-    * @return {void}
-    */
-   static finalize() {
-     if (!this.hasOwnProperty(JSCompiler_renameProperty('__finalized', this))) {
-       const superCtor = superPropertiesClass(/** @type {!PropertiesMixinConstructor} */(this));
-       if (superCtor) {
-         superCtor.finalize();
-       }
-       this.__finalized = true;
-       this._finalizeClass();
-     }
-   }
-
-   /**
-    * Finalize an element class. This includes ensuring property
-    * accessors exist on the element prototype. This method is called by
-    * `finalize` and finalizes the class constructor.
-    *
-    * @protected
-    */
-   static _finalizeClass() {
-     const props = ownProperties(/** @type {!PropertiesMixinConstructor} */(this));
-     if (props) {
-       this.createProperties(props);
-     }
-   }
-
-   /**
-    * Returns a memoized version of all properties, including those inherited
-    * from super classes. Properties not in object format are converted to
-    * at least {type}.
-    *
-    * @return {Object} Object containing properties for this class
-    * @protected
-    */
-   static get _properties() {
-     if (!this.hasOwnProperty(
-       JSCompiler_renameProperty('__properties', this))) {
-       const superCtor = superPropertiesClass(/** @type {!PropertiesMixinConstructor} */(this));
-       this.__properties = Object.assign({},
-         superCtor && superCtor._properties,
-         ownProperties(/** @type {PropertiesMixinConstructor} */(this)));
-     }
-     return this.__properties;
-   }
-
-   /**
-    * Overrides `PropertiesChanged` method to return type specified in the
-    * static `properties` object for the given property.
-    * @param {string} name Name of property
-    * @return {*} Type to which to deserialize attribute
-    *
-    * @protected
-    */
-   static typeForProperty(name) {
-     const info = this._properties[name];
-     return info && info.type;
-   }
-
-   /**
-    * Overrides `PropertiesChanged` method and adds a call to
-    * `finalize` which lazily configures the element's property accessors.
-    * @override
-    * @return {void}
-    */
-   _initializeProperties() {
-     incrementInstanceCount();
-     this.constructor.finalize();
-     super._initializeProperties();
-   }
-
-   /**
-    * Called when the element is added to a document.
-    * Calls `_enableProperties` to turn on property system from
-    * `PropertiesChanged`.
-    * @suppress {missingProperties} Super may or may not implement the callback
-    * @return {void}
-    * @override
-    */
-   connectedCallback() {
-     if (super.connectedCallback) {
-       super.connectedCallback();
-     }
-     this._enableProperties();
-   }
-
-   /**
-    * Called when the element is removed from a document
-    * @suppress {missingProperties} Super may or may not implement the callback
-    * @return {void}
-    * @override
-    */
-   disconnectedCallback() {
-     if (super.disconnectedCallback) {
-       super.disconnectedCallback();
-     }
-   }
-
- }
-
- return PropertiesMixin;
-
-});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-accessors.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-accessors.js
deleted file mode 100644
index bfa7f33a..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-accessors.js
+++ /dev/null
@@ -1,323 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-
-import { dedupingMixin } from '../utils/mixin.js';
-import { camelToDashCase, dashToCamelCase } from '../utils/case-map.js';
-import { PropertiesChanged } from './properties-changed.js';
-
-// Save map of native properties; this forms a blacklist or properties
-// that won't have their values "saved" by `saveAccessorValue`, since
-// reading from an HTMLElement accessor from the context of a prototype throws
-const nativeProperties = {};
-let proto = HTMLElement.prototype;
-while (proto) {
-  let props = Object.getOwnPropertyNames(proto);
-  for (let i=0; i<props.length; i++) {
-    nativeProperties[props[i]] = true;
-  }
-  proto = Object.getPrototypeOf(proto);
-}
-
-/**
- * Used to save the value of a property that will be overridden with
- * an accessor. If the `model` is a prototype, the values will be saved
- * in `__dataProto`, and it's up to the user (or downstream mixin) to
- * decide how/when to set these values back into the accessors.
- * If `model` is already an instance (it has a `__data` property), then
- * the value will be set as a pending property, meaning the user should
- * call `_invalidateProperties` or `_flushProperties` to take effect
- *
- * @param {Object} model Prototype or instance
- * @param {string} property Name of property
- * @return {void}
- * @private
- */
-function saveAccessorValue(model, property) {
-  // Don't read/store value for any native properties since they could throw
-  if (!nativeProperties[property]) {
-    let value = model[property];
-    if (value !== undefined) {
-      if (model.__data) {
-        // Adding accessor to instance; update the property
-        // It is the user's responsibility to call _flushProperties
-        model._setPendingProperty(property, value);
-      } else {
-        // Adding accessor to proto; save proto's value for instance-time use
-        if (!model.__dataProto) {
-          model.__dataProto = {};
-        } else if (!model.hasOwnProperty(JSCompiler_renameProperty('__dataProto', model))) {
-          model.__dataProto = Object.create(model.__dataProto);
-        }
-        model.__dataProto[property] = value;
-      }
-    }
-  }
-}
-
-/**
- * Element class mixin that provides basic meta-programming for creating one
- * or more property accessors (getter/setter pair) that enqueue an async
- * (batched) `_propertiesChanged` callback.
- *
- * For basic usage of this mixin:
- *
- * -   Declare attributes to observe via the standard `static get
- *     observedAttributes()`. Use `dash-case` attribute names to represent
- *     `camelCase` property names.
- * -   Implement the `_propertiesChanged` callback on the class.
- * -   Call `MyClass.createPropertiesForAttributes()` **once** on the class to
- *     generate property accessors for each observed attribute. This must be
- *     called before the first instance is created, for example, by calling it
- *     before calling `customElements.define`. It can also be called lazily from
- *     the element's `constructor`, as long as it's guarded so that the call is
- *     only made once, when the first instance is created.
- * -   Call `this._enableProperties()` in the element's `connectedCallback` to
- *     enable the accessors.
- *
- * Any `observedAttributes` will automatically be
- * deserialized via `attributeChangedCallback` and set to the associated
- * property using `dash-case`-to-`camelCase` convention.
- *
- * @mixinFunction
- * @polymer
- * @appliesMixin PropertiesChanged
- * @summary Element class mixin for reacting to property changes from
- *   generated property accessors.
- */
-export const PropertyAccessors = dedupingMixin(superClass => {
-
-  /**
-   * @constructor
-   * @implements {Polymer_PropertiesChanged}
-   * @unrestricted
-   * @private
-   */
-   const base = PropertiesChanged(superClass);
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_PropertyAccessors}
-   * @extends {base}
-   * @unrestricted
-   */
-  class PropertyAccessors extends base {
-
-    /**
-     * Generates property accessors for all attributes in the standard
-     * static `observedAttributes` array.
-     *
-     * Attribute names are mapped to property names using the `dash-case` to
-     * `camelCase` convention
-     *
-     * @return {void}
-     */
-    static createPropertiesForAttributes() {
-      let a$ = this.observedAttributes;
-      for (let i=0; i < a$.length; i++) {
-        this.prototype._createPropertyAccessor(dashToCamelCase(a$[i]));
-      }
-    }
-
-    /**
-     * Returns an attribute name that corresponds to the given property.
-     * By default, converts camel to dash case, e.g. `fooBar` to `foo-bar`.
-     * @param {string} property Property to convert
-     * @return {string} Attribute name corresponding to the given property.
-     *
-     * @protected
-     */
-    static attributeNameForProperty(property) {
-      return camelToDashCase(property);
-    }
-
-    /**
-     * Overrides PropertiesChanged implementation to initialize values for
-     * accessors created for values that already existed on the element
-     * prototype.
-     *
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _initializeProperties() {
-      if (this.__dataProto) {
-        this._initializeProtoProperties(this.__dataProto);
-        this.__dataProto = null;
-      }
-      super._initializeProperties();
-    }
-
-    /**
-     * Called at instance time with bag of properties that were overwritten
-     * by accessors on the prototype when accessors were created.
-     *
-     * The default implementation sets these properties back into the
-     * setter at instance time.  This method is provided as an override
-     * point for customizing or providing more efficient initialization.
-     *
-     * @param {Object} props Bag of property values that were overwritten
-     *   when creating property accessors.
-     * @return {void}
-     * @protected
-     * @override
-     */
-    _initializeProtoProperties(props) {
-      for (let p in props) {
-        this._setProperty(p, props[p]);
-      }
-    }
-
-    /**
-     * Ensures the element has the given attribute. If it does not,
-     * assigns the given value to the attribute.
-     *
-     * @suppress {invalidCasts} Closure can't figure out `this` is infact an
-     *     element
-     *
-     * @param {string} attribute Name of attribute to ensure is set.
-     * @param {string} value of the attribute.
-     * @return {void}
-     * @override
-     */
-    _ensureAttribute(attribute, value) {
-      const el = /** @type {!HTMLElement} */(this);
-      if (!el.hasAttribute(attribute)) {
-        this._valueToNodeAttribute(el, value, attribute);
-      }
-    }
-
-    /**
-     * Overrides PropertiesChanged implemention to serialize objects as JSON.
-     *
-     * @param {*} value Property value to serialize.
-     * @return {string | undefined} String serialized from the provided property
-     *     value.
-     * @override
-     */
-    _serializeValue(value) {
-      /* eslint-disable no-fallthrough */
-      switch (typeof value) {
-        case 'object':
-          if (value instanceof Date) {
-            return value.toString();
-          } else if (value) {
-            try {
-              return JSON.stringify(value);
-            } catch(x) {
-              return '';
-            }
-          }
-
-        default:
-          return super._serializeValue(value);
-      }
-    }
-
-    /**
-     * Converts a string to a typed JavaScript value.
-     *
-     * This method is called by Polymer when reading HTML attribute values to
-     * JS properties.  Users may override this method on Polymer element
-     * prototypes to provide deserialization for custom `type`s.  Note,
-     * the `type` argument is the value of the `type` field provided in the
-     * `properties` configuration object for a given property, and is
-     * by convention the constructor for the type to deserialize.
-     *
-     *
-     * @param {?string} value Attribute value to deserialize.
-     * @param {*=} type Type to deserialize the string to.
-     * @return {*} Typed value deserialized from the provided string.
-     * @override
-     */
-    _deserializeValue(value, type) {
-      /**
-       * @type {*}
-       */
-      let outValue;
-      switch (type) {
-        case Object:
-          try {
-            outValue = JSON.parse(/** @type {string} */(value));
-          } catch(x) {
-            // allow non-JSON literals like Strings and Numbers
-            outValue = value;
-          }
-          break;
-        case Array:
-          try {
-            outValue = JSON.parse(/** @type {string} */(value));
-          } catch(x) {
-            outValue = null;
-            console.warn(`Polymer::Attributes: couldn't decode Array as JSON: ${value}`);
-          }
-          break;
-        case Date:
-          outValue = isNaN(value) ? String(value) : Number(value);
-          outValue = new Date(outValue);
-          break;
-        default:
-          outValue = super._deserializeValue(value, type);
-          break;
-      }
-      return outValue;
-    }
-    /* eslint-enable no-fallthrough */
-
-    /**
-     * Overrides PropertiesChanged implementation to save existing prototype
-     * property value so that it can be reset.
-     * @param {string} property Name of the property
-     * @param {boolean=} readOnly When true, no setter is created
-     *
-     * When calling on a prototype, any overwritten values are saved in
-     * `__dataProto`, and it is up to the subclasser to decide how/when
-     * to set those properties back into the accessor.  When calling on an
-     * instance, the overwritten value is set via `_setPendingProperty`,
-     * and the user should call `_invalidateProperties` or `_flushProperties`
-     * for the values to take effect.
-     * @protected
-     * @return {void}
-     * @override
-     */
-    _definePropertyAccessor(property, readOnly) {
-      saveAccessorValue(this, property);
-      super._definePropertyAccessor(property, readOnly);
-    }
-
-    /**
-     * Returns true if this library created an accessor for the given property.
-     *
-     * @param {string} property Property name
-     * @return {boolean} True if an accessor was created
-     * @override
-     */
-    _hasAccessor(property) {
-      return this.__dataHasAccessor && this.__dataHasAccessor[property];
-    }
-
-    /**
-     * Returns true if the specified property has a pending change.
-     *
-     * @param {string} prop Property name
-     * @return {boolean} True if property has a pending change
-     * @protected
-     * @override
-     */
-    _isPropertyPending(prop) {
-      return Boolean(this.__dataPending && (prop in this.__dataPending));
-    }
-
-  }
-
-  return PropertyAccessors;
-
-});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-effects.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-effects.js
deleted file mode 100644
index c92bea3..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-effects.js
+++ /dev/null
@@ -1,2856 +0,0 @@
-/**
- * @fileoverview
- * @suppress {checkPrototypalTypes}
- * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at
- * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
- * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
- * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
- * Google as part of the polymer project is also subject to an additional IP
- * rights grant found at http://polymer.github.io/PATENTS.txt
- */
-
-import '../utils/boot.js';
-import { wrap } from '../utils/wrap.js';
-import { dedupingMixin } from '../utils/mixin.js';
-import { root, isAncestor, isDescendant, get, translate, isPath, set, normalize } from '../utils/path.js';
-/* for notify, reflect */
-import { camelToDashCase, dashToCamelCase } from '../utils/case-map.js';
-import { PropertyAccessors } from './property-accessors.js';
-/* for annotated effects */
-import { TemplateStamp } from './template-stamp.js';
-import { sanitizeDOMValue } from '../utils/settings.js';
-
-// Monotonically increasing unique ID used for de-duping effects triggered
-// from multiple properties in the same turn
-let dedupeId = 0;
-
-/**
- * Property effect types; effects are stored on the prototype using these keys
- * @enum {string}
- */
-const TYPES = {
-  COMPUTE: '__computeEffects',
-  REFLECT: '__reflectEffects',
-  NOTIFY: '__notifyEffects',
-  PROPAGATE: '__propagateEffects',
-  OBSERVE: '__observeEffects',
-  READ_ONLY: '__readOnly'
-};
-
-/** @const {!RegExp} */
-const capitalAttributeRegex = /[A-Z]/;
-
-/**
- * @typedef {{
- * name: (string | undefined),
- * structured: (boolean | undefined),
- * wildcard: (boolean | undefined)
- * }}
- */
-let DataTrigger; //eslint-disable-line no-unused-vars
-
-/**
- * @typedef {{
- * info: ?,
- * trigger: (!DataTrigger | undefined),
- * fn: (!Function | undefined)
- * }}
- */
-let DataEffect; //eslint-disable-line no-unused-vars
-
-/**
- * Ensures that the model has an own-property map of effects for the given type.
- * The model may be a prototype or an instance.
- *
- * Property effects are stored as arrays of effects by property in a map,
- * by named type on the model. e.g.
- *
- *   __computeEffects: {
- *     foo: [ ... ],
- *     bar: [ ... ]
- *   }
- *
- * If the model does not yet have an effect map for the type, one is created
- * and returned.  If it does, but it is not an own property (i.e. the
- * prototype had effects), the the map is deeply cloned and the copy is
- * set on the model and returned, ready for new effects to be added.
- *
- * @param {Object} model Prototype or instance
- * @param {string} type Property effect type
- * @return {Object} The own-property map of effects for the given type
- * @private
- */
-function ensureOwnEffectMap(model, type) {
-  let effects = model[type];
-  if (!effects) {
-    effects = model[type] = {};
-  } else if (!model.hasOwnProperty(type)) {
-    effects = model[type] = Object.create(model[type]);
-    for (let p in effects) {
-      let protoFx = effects[p];
-      let instFx = effects[p] = Array(protoFx.length);
-      for (let i=0; i<protoFx.length; i++) {
-        instFx[i] = protoFx[i];
-      }
-    }
-  }
-  return effects;
-}
-
-// -- effects ----------------------------------------------
-
-/**
- * Runs all effects of a given type for the given set of property changes
- * on an instance.
- *
- * @param {!Polymer_PropertyEffects} inst The instance with effects to run
- * @param {?Object} effects Object map of property-to-Array of effects
- * @param {?Object} props Bag of current property changes
- * @param {?Object=} oldProps Bag of previous values for changed properties
- * @param {boolean=} hasPaths True with `props` contains one or more paths
- * @param {*=} extraArgs Additional metadata to pass to effect function
- * @return {boolean} True if an effect ran for this property
- * @private
- */
-function runEffects(inst, effects, props, oldProps, hasPaths, extraArgs) {
-  if (effects) {
-    let ran = false;
-    let id = dedupeId++;
-    for (let prop in props) {
-      if (runEffectsForProperty(
-              inst, /** @type {!Object} */ (effects), id, prop, props, oldProps,
-              hasPaths, extraArgs)) {
-        ran = true;
-      }
-    }
-    return ran;
-  }
-  return false;
-}
-
-/**
- * Runs a list of effects for a given property.
- *
- * @param {!Polymer_PropertyEffects} inst The instance with effects to run
- * @param {!Object} effects Object map of property-to-Array of effects
- * @param {number} dedupeId Counter used for de-duping effects
- * @param {string} prop Name of changed property
- * @param {*} props Changed properties
- * @param {*} oldProps Old properties
- * @param {boolean=} hasPaths True with `props` contains one or more paths
- * @param {*=} extraArgs Additional metadata to pass to effect function
- * @return {boolean} True if an effect ran for this property
- * @private
- */
-function runEffectsForProperty(inst, effects, dedupeId, prop, props, oldProps, hasPaths, extraArgs) {
-  let ran = false;
-  let rootProperty = hasPaths ? root(prop) : prop;
-  let fxs = effects[rootProperty];
-  if (fxs) {
-    for (let i=0, l=fxs.length, fx; (i<l) && (fx=fxs[i]); i++) {
-      if ((!fx.info || fx.info.lastRun !== dedupeId) &&
-          (!hasPaths || pathMatchesTrigger(prop, fx.trigger))) {
-        if (fx.info) {
-          fx.info.lastRun = dedupeId;
-        }
-        fx.fn(inst, prop, props, oldProps, fx.info, hasPaths, extraArgs);
-        ran = true;
-      }
-    }
-  }
-  return ran;
-}
-
-/**
- * Determines whether a property/path that has changed matches the trigger
- * criteria for an effect.  A trigger is a descriptor with the following
- * structure, which matches the descriptors returned from `parseArg`.
- * e.g. for `foo.bar.*`:
- * ```
- * trigger: {
- *   name: 'a.b',
- *   structured: true,
- *   wildcard: true
- * }
- * ```
- * If no trigger is given, the path is deemed to match.
- *
- * @param {string} path Path or property that changed
- * @param {?DataTrigger} trigger Descriptor
- * @return {boolean} Whether the path matched the trigger
- */
-function pathMatchesTrigger(path, trigger) {
-  if (trigger) {
-    let triggerPath = /** @type {string} */ (trigger.name);
-    return (triggerPath == path) ||
-        !!(trigger.structured && isAncestor(triggerPath, path)) ||
-        !!(trigger.wildcard && isDescendant(triggerPath, path));
-  } else {
-    return true;
-  }
-}
-
-/**
- * Implements the "observer" effect.
- *
- * Calls the method with `info.methodName` on the instance, passing the
- * new and old values.
- *
- * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
- * @param {string} property Name of property
- * @param {Object} props Bag of current property changes
- * @param {Object} oldProps Bag of previous values for changed properties
- * @param {?} info Effect metadata
- * @return {void}
- * @private
- */
-function runObserverEffect(inst, property, props, oldProps, info) {
-  let fn = typeof info.method === "string" ? inst[info.method] : info.method;
-  let changedProp = info.property;
-  if (fn) {
-    fn.call(inst, inst.__data[changedProp], oldProps[changedProp]);
-  } else if (!info.dynamicFn) {
-    console.warn('observer method `' + info.method + '` not defined');
-  }
-}
-
-/**
- * Runs "notify" effects for a set of changed properties.
- *
- * This method differs from the generic `runEffects` method in that it
- * will dispatch path notification events in the case that the property
- * changed was a path and the root property for that path didn't have a
- * "notify" effect.  This is to maintain 1.0 behavior that did not require
- * `notify: true` to ensure object sub-property notifications were
- * sent.
- *
- * @param {!Polymer_PropertyEffects} inst The instance with effects to run
- * @param {Object} notifyProps Bag of properties to notify
- * @param {Object} props Bag of current property changes
- * @param {Object} oldProps Bag of previous values for changed properties
- * @param {boolean} hasPaths True with `props` contains one or more paths
- * @return {void}
- * @private
- */
-function runNotifyEffects(inst, notifyProps, props, oldProps, hasPaths) {
-  // Notify
-  let fxs = inst[TYPES.NOTIFY];
-  let notified;
-  let id = dedupeId++;
-  // Try normal notify effects; if none, fall back to try path notification
-  for (let prop in notifyProps) {
-    if (notifyProps[prop]) {
-      if (fxs && runEffectsForProperty(inst, fxs, id, prop, props, oldProps, hasPaths)) {
-        notified = true;
-      } else if (hasPaths && notifyPath(inst, prop, props)) {
-        notified = true;
-      }
-    }
-  }
-  // Flush host if we actually notified and host was batching
-  // And the host has already initialized clients; this prevents
-  // an issue with a host observing data changes before clients are ready.
-  let host;
-  if (notified && (host = inst.__dataHost) && host._invalidateProperties) {
-    host._invalidateProperties();
-  }
-}
-
-/**
- * Dispatches {property}-changed events with path information in the detail
- * object to indicate a sub-path of the property was changed.
- *
- * @param {!Polymer_PropertyEffects} inst The element from which to fire the
- *     event
- * @param {string} path The path that was changed
- * @param {Object} props Bag of current property changes
- * @return {boolean} Returns true if the path was notified
- * @private
- */
-function notifyPath(inst, path, props) {
-  let rootProperty = root(path);
-  if (rootProperty !== path) {
-    let eventName = camelToDashCase(rootProperty) + '-changed';
-    dispatchNotifyEvent(inst, eventName, props[path], path);
-    return true;
-  }
-  return false;
-}
-
-/**
- * Dispatches {property}-changed events to indicate a property (or path)
- * changed.
- *
- * @param {!Polymer_PropertyEffects} inst The element from which to fire the
- *     event
- * @param {string} eventName The name of the event to send
- *     ('{property}-changed')
- * @param {*} value The value of the changed property
- * @param {string | null | undefined} path If a sub-path of this property
- *     changed, the path that changed (optional).
- * @return {void}
- * @private
- * @suppress {invalidCasts}
- */
-function dispatchNotifyEvent(inst, eventName, value, path) {
-  let detail = {
-    value: value,
-    queueProperty: true
-  };
-  if (path) {
-    detail.path = path;
-  }
-  wrap(/** @type {!HTMLElement} */(inst)).dispatchEvent(new CustomEvent(eventName, { detail }));
-}
-
-/**
- * Implements the "notify" effect.
- *
- * Dispatches a non-bubbling event named `info.eventName` on the instance
- * with a detail object containing the new `value`.
- *
- * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
- * @param {string} property Name of property
- * @param {Object} props Bag of current property changes
- * @param {Object} oldProps Bag of previous values for changed properties
- * @param {?} info Effect metadata
- * @param {boolean} hasPaths True with `props` contains one or more paths
- * @return {void}
- * @private
- */
-function runNotifyEffect(inst, property, props, oldProps, info, hasPaths) {
-  let rootProperty = hasPaths ? root(property) : property;
-  let path = rootProperty != property ? property : null;
-  let value = path ? get(inst, path) : inst.__data[property];
-  if (path && value === undefined) {
-    value = props[property];  // specifically for .splices
-  }
-  dispatchNotifyEvent(inst, info.eventName, value, path);
-}
-
-/**
- * Handler function for 2-way notification events. Receives context
- * information captured in the `addNotifyListener` closure from the
- * `__notifyListeners` metadata.
- *
- * Sets the value of the notified property to the host property or path.  If
- * the event contained path information, translate that path to the host
- * scope's name for that path first.
- *
- * @param {CustomEvent} event Notification event (e.g. '<property>-changed')
- * @param {!Polymer_PropertyEffects} inst Host element instance handling the
- *     notification event
- * @param {string} fromProp Child element property that was bound
- * @param {string} toPath Host property/path that was bound
- * @param {boolean} negate Whether the binding was negated
- * @return {void}
- * @private
- */
-function handleNotification(event, inst, fromProp, toPath, negate) {
-  let value;
-  let detail = /** @type {Object} */(event.detail);
-  let fromPath = detail && detail.path;
-  if (fromPath) {
-    toPath = translate(fromProp, toPath, fromPath);
-    value = detail && detail.value;
-  } else {
-    value = event.currentTarget[fromProp];
-  }
-  value = negate ? !value : value;
-  if (!inst[TYPES.READ_ONLY] || !inst[TYPES.READ_ONLY][toPath]) {
-    if (inst._setPendingPropertyOrPath(toPath, value, true, Boolean(fromPath))
-      && (!detail || !detail.queueProperty)) {
-      inst._invalidateProperties();
-    }
-  }
-}
-
-/**
- * Implements the "reflect" effect.
- *
- * Sets the attribute named `info.attrName` to the given property value.
- *
- * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
- * @param {string} property Name of property
- * @param {Object} props Bag of current property changes
- * @param {Object} oldProps Bag of previous values for changed properties
- * @param {?} info Effect metadata
- * @return {void}
- * @private
- */
-function runReflectEffect(inst, property, props, oldProps, info) {
-  let value = inst.__data[property];
-  if (sanitizeDOMValue) {
-    value = sanitizeDOMValue(value, info.attrName, 'attribute', /** @type {Node} */(inst));
-  }
-  inst._propertyToAttribute(property, info.attrName, value);
-}
-
-/**
- * Runs "computed" effects for a set of changed properties.
- *
- * This method differs from the generic `runEffects` method in that it
- * continues to run computed effects based on the output of each pass until
- * there are no more newly computed properties.  This ensures that all
- * properties that will be computed by the initial set of changes are
- * computed before other effects (binding propagation, observers, and notify)
- * run.
- *
- * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
- * @param {?Object} changedProps Bag of changed properties
- * @param {?Object} oldProps Bag of previous values for changed properties
- * @param {boolean} hasPaths True with `props` contains one or more paths
- * @return {void}
- * @private
- */
-function runComputedEffects(inst, changedProps, oldProps, hasPaths) {
-  let computeEffects = inst[TYPES.COMPUTE];
-  if (computeEffects) {
-    let inputProps = changedProps;
-    while (runEffects(inst, computeEffects, inputProps, oldProps, hasPaths)) {
-      Object.assign(/** @type {!Object} */ (oldProps), inst.__dataOld);
-      Object.assign(/** @type {!Object} */ (changedProps), inst.__dataPending);
-      inputProps = inst.__dataPending;
-      inst.__dataPending = null;
-    }
-  }
-}
-
-/**
- * Implements the "computed property" effect by running the method with the
- * values of the arguments specified in the `info` object and setting the
- * return value to the computed property specified.
- *
- * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
- * @param {string} property Name of property
- * @param {?Object} props Bag of current property changes
- * @param {?Object} oldProps Bag of previous values for changed properties
- * @param {?} info Effect metadata
- * @return {void}
- * @private
- */
-function runComputedEffect(inst, property, props, oldProps, info) {
-  let result = runMethodEffect(inst, property, props, oldProps, info);
-  let computedProp = info.methodInfo;
-  if (inst.__dataHasAccessor && inst.__dataHasAccessor[computedProp]) {
-    inst._setPendingProperty(computedProp, result, true);
-  } else {
-    inst[computedProp] = result;
-  }
-}
-
-/**
- * Computes path changes based on path links set up using the `linkPaths`
- * API.
- *
- * @param {!Polymer_PropertyEffects} inst The instance whose props are changing
- * @param {string} path Path that has changed
- * @param {*} value Value of changed path
- * @return {void}
- * @private
- */
-function computeLinkedPaths(inst, path, value) {
-  let links = inst.__dataLinkedPaths;
-  if (links) {
-    let link;
-    for (let a in links) {
-      let b = links[a];
-      if (isDescendant(a, path)) {
-        link = translate(a, b, path);
-        inst._setPendingPropertyOrPath(link, value, true, true);
-      } else if (isDescendant(b, path)) {
-        link = translate(b, a, path);
-        inst._setPendingPropertyOrPath(link, value, true, true);
-      }
-    }
-  }
-}
-
-// -- bindings ----------------------------------------------
-
-/**
- * Adds binding metadata to the current `nodeInfo`, and binding effects
- * for all part dependencies to `templateInfo`.
- *
- * @param {Function} constructor Class that `_parseTemplate` is currently
- *   running on
- * @param {TemplateInfo} templateInfo Template metadata for current template
- * @param {NodeInfo} nodeInfo Node metadata for current template node
- * @param {string} kind Binding kind, either 'property', 'attribute', or 'text'
- * @param {string} target Target property name
- * @param {!Array<!BindingPart>} parts Array of binding part metadata
- * @param {string=} literal Literal text surrounding binding parts (specified
- *   only for 'property' bindings, since these must be initialized as part
- *   of boot-up)
- * @return {void}
- * @private
- */
-function addBinding(constructor, templateInfo, nodeInfo, kind, target, parts, literal) {
-  // Create binding metadata and add to nodeInfo
-  nodeInfo.bindings = nodeInfo.bindings || [];
-  let /** Binding */ binding = { kind, target, parts, literal, isCompound: (parts.length !== 1) };
-  nodeInfo.bindings.push(binding);
-  // Add listener info to binding metadata
-  if (shouldAddListener(binding)) {
-    let {event, negate} = binding.parts[0];
-    binding.listenerEvent = event || (camelToDashCase(target) + '-changed');
-    binding.listenerNegate = negate;
-  }
-  // Add "propagate" property effects to templateInfo
-  let index = templateInfo.nodeInfoList.length;
-  for (let i=0; i<binding.parts.length; i++) {
-    let part = binding.parts[i];
-    part.compoundIndex = i;
-    addEffectForBindingPart(constructor, templateInfo, binding, part, index);
-  }
-}
-
-/**
- * Adds property effects to the given `templateInfo` for the given binding
- * part.
- *
- * @param {Function} constructor Class that `_parseTemplate` is currently
- *   running on
- * @param {TemplateInfo} templateInfo Template metadata for current template
- * @param {!Binding} binding Binding metadata
- * @param {!BindingPart} part Binding part metadata
- * @param {number} index Index into `nodeInfoList` for this node
- * @return {void}
- */
-function addEffectForBindingPart(constructor, templateInfo, binding, part, index) {
-  if (!part.literal) {
-    if (binding.kind === 'attribute' && binding.target[0] === '-') {
-      console.warn('Cannot set attribute ' + binding.target +
-        ' because "-" is not a valid attribute starting character');
-    } else {
-      let dependencies = part.dependencies;
-      let info = { index, binding, part, evaluator: constructor };
-      for (let j=0; j<dependencies.length; j++) {
-        let trigger = dependencies[j];
-        if (typeof trigger == 'string') {
-          trigger = parseArg(trigger);
-          trigger.wildcard = true;
-        }
-        constructor._addTemplatePropertyEffect(templateInfo, trigger.rootProperty, {
-          fn: runBindingEffect,
-          info, trigger
-        });
-      }
-    }
-  }
-}
-
-/**
- * Implements the "binding" (property/path binding) effect.
- *
- * Note that binding syntax is overridable via `_parseBindings` and
- * `_evaluateBinding`.  This method will call `_evaluateBinding` for any
- * non-literal parts returned from `_parseBindings`.  However,
- * there is no support for _path_ bindings via custom binding parts,
- * as this is specific to Polymer's path binding syntax.
- *
- * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
- * @param {string} path Name of property
- * @param {Object} props Bag of current property changes
- * @param {Object} oldProps Bag of previous values for changed properties
- * @param {?} info Effect metadata
- * @param {boolean} hasPaths True with `props` contains one or more paths
- * @param {Array} nodeList List of nodes associated with `nodeInfoList` template
- *   metadata
- * @return {void}
- * @private
- */
-function runBindingEffect(inst, path, props, oldProps, info, hasPaths, nodeList) {
-  let node = nodeList[info.index];
-  let binding = info.binding;
-  let part = info.part;
-  // Subpath notification: transform path and set to client
-  // e.g.: foo="{{obj.sub}}", path: 'obj.sub.prop', set 'foo.prop'=obj.sub.prop
-  if (hasPaths && part.source && (path.length > part.source.length) &&
-      (binding.kind == 'property') && !binding.isCompound &&
-      node.__isPropertyEffectsClient &&
-      node.__dataHasAccessor && node.__dataHasAccessor[binding.target]) {
-    let value = props[path];
-    path = translate(part.source, binding.target, path);
-    if (node._setPendingPropertyOrPath(path, value, false, true)) {
-      inst._enqueueClient(node);
-    }
-  } else {
-    let value = info.evaluator._evaluateBinding(inst, part, path, props, oldProps, hasPaths);
-    // Propagate value to child
-    applyBindingValue(inst, node, binding, part, value);
-  }
-}
-
-/**
- * Sets the value for an "binding" (binding) effect to a node,
- * either as a property or attribute.
- *
- * @param {!Polymer_PropertyEffects} inst The instance owning the binding effect
- * @param {Node} node Target node for binding
- * @param {!Binding} binding Binding metadata
- * @param {!BindingPart} part Binding part metadata
- * @param {*} value Value to set
- * @return {void}
- * @private
- */
-function applyBindingValue(inst, node, binding, part, value) {
-  value = computeBindingValue(node, value, binding, part);
-  if (sanitizeDOMValue) {
-    value = sanitizeDOMValue(value, binding.target, binding.kind, node);
-  }
-  if (binding.kind == 'attribute') {
-    // Attribute binding
-    inst._valueToNodeAttribute(/** @type {Element} */(node), value, binding.target);
-  } else {
-    // Property binding
-    let prop = binding.target;
-    if (node.__isPropertyEffectsClient &&
-        node.__dataHasAccessor && node.__dataHasAccessor[prop]) {
-      if (!node[TYPES.READ_ONLY] || !node[TYPES.READ_ONLY][prop]) {
-        if (node._setPendingProperty(prop, value)) {
-          inst._enqueueClient(node);
-        }
-      }
-    } else  {
-      inst._setUnmanagedPropertyToNode(node, prop, value);
-    }
-  }
-}
-
-/**
- * Transforms an "binding" effect value based on compound & negation
- * effect metadata, as well as handling for special-case properties
- *
- * @param {Node} node Node the value will be set to
- * @param {*} value Value to set
- * @param {!Binding} binding Binding metadata
- * @param {!BindingPart} part Binding part metadata
- * @return {*} Transformed value to set
- * @private
- */
-function computeBindingValue(node, value, binding, part) {
-  if (binding.isCompound) {
-    let storage = node.__dataCompoundStorage[binding.target];
-    storage[part.compoundIndex] = value;
-    value = storage.join('');
-  }
-  if (binding.kind !== 'attribute') {
-    // Some browsers serialize `undefined` to `"undefined"`
-    if (binding.target === 'textContent' ||
-        (binding.target === 'value' &&
-          (node.localName === 'input' || node.localName === 'textarea'))) {
-      value = value == undefined ? '' : value;
-    }
-  }
-  return value;
-}
-
-/**
- * Returns true if a binding's metadata meets all the requirements to allow
- * 2-way binding, and therefore a `<property>-changed` event listener should be
- * added:
- * - used curly braces
- * - is a property (not attribute) binding
- * - is not a textContent binding
- * - is not compound
- *
- * @param {!Binding} binding Binding metadata
- * @return {boolean} True if 2-way listener should be added
- * @private
- */
-function shouldAddListener(binding) {
-  return Boolean(binding.target) &&
-         binding.kind != 'attribute' &&
-         binding.kind != 'text' &&
-         !binding.isCompound &&
-         binding.parts[0].mode === '{';
-}
-
-/**
- * Setup compound binding storage structures, notify listeners, and dataHost
- * references onto the bound nodeList.
- *
- * @param {!Polymer_PropertyEffects} inst Instance that bas been previously
- *     bound
- * @param {TemplateInfo} templateInfo Template metadata
- * @return {void}
- * @private
- */
-function setupBindings(inst, templateInfo) {
-  // Setup compound storage, dataHost, and notify listeners
-  let {nodeList, nodeInfoList} = templateInfo;
-  if (nodeInfoList.length) {
-    for (let i=0; i < nodeInfoList.length; i++) {
-      let info = nodeInfoList[i];
-      let node = nodeList[i];
-      let bindings = info.bindings;
-      if (bindings) {
-        for (let i=0; i<bindings.length; i++) {
-          let binding = bindings[i];
-          setupCompoundStorage(node, binding);
-          addNotifyListener(node, inst, binding);
-        }
-      }
-      node.__dataHost = inst;
-    }
-  }
-}
-
-/**
- * Initializes `__dataCompoundStorage` local storage on a bound node with
- * initial literal data for compound bindings, and sets the joined
- * literal parts to the bound property.
- *
- * When changes to compound parts occur, they are first set into the compound
- * storage array for that property, and then the array is joined to result in
- * the final value set to the property/attribute.
- *
- * @param {Node} node Bound node to initialize
- * @param {Binding} binding Binding metadata
- * @return {void}
- * @private
- */
-function setupCompoundStorage(node, binding) {
-  if (binding.isCompound) {
-    // Create compound storage map
-    let storage = node.__dataCompoundStorage ||
-      (node.__dataCompoundStorage = {});
-    let parts = binding.parts;
-    // Copy literals from parts into storage for this binding
-    let literals = new Array(parts.length);
-    for (let j=0; j<parts.length; j++) {
-      literals[j] = parts[j].literal;
-    }
-    let target = binding.target;
-    storage[target] = literals;
-    // Configure properties with their literal parts
-    if (binding.literal && binding.kind == 'property') {
-      node[target] = binding.literal;
-    }
-  }
-}
-
-/**
- * Adds a 2-way binding notification event listener to the node specified
- *
- * @param {Object} node Child element to add listener to
- * @param {!Polymer_PropertyEffects} inst Host element instance to handle
- *     notification event
- * @param {Binding} binding Binding metadata
- * @return {void}
- * @private
- */
-function addNotifyListener(node, inst, binding) {
-  if (binding.listenerEvent) {
-    let part = binding.parts[0];
-    node.addEventListener(binding.listenerEvent, function(e) {
-      handleNotification(e, inst, binding.target, part.source, part.negate);
-    });
-  }
-}
-
-// -- for method-based effects (complexObserver & computed) --------------
-
-/**
- * Adds property effects for each argument in the method signature (and
- * optionally, for the method name if `dynamic` is true) that calls the
- * provided effect function.
- *
- * @param {Element | Object} model Prototype or instance
- * @param {!MethodSignature} sig Method signature metadata
- * @param {string} type Type of property effect to add
- * @param {Function} effectFn Function to run when arguments change
- * @param {*=} methodInfo Effect-specific information to be included in
- *   method effect metadata
- * @param {boolean|Object=} dynamicFn Boolean or object map indicating whether
- *   method names should be included as a dependency to the effect. Note,
- *   defaults to true if the signature is static (sig.static is true).
- * @return {void}
- * @private
- */
-function createMethodEffect(model, sig, type, effectFn, methodInfo, dynamicFn) {
-  dynamicFn = sig.static || (dynamicFn &&
-    (typeof dynamicFn !== 'object' || dynamicFn[sig.methodName]));
-  let info = {
-    methodName: sig.methodName,
-    args: sig.args,
-    methodInfo,
-    dynamicFn
-  };
-  for (let i=0, arg; (i<sig.args.length) && (arg=sig.args[i]); i++) {
-    if (!arg.literal) {
-      model._addPropertyEffect(arg.rootProperty, type, {
-        fn: effectFn, info: info, trigger: arg
-      });
-    }
-  }
-  if (dynamicFn) {
-    model._addPropertyEffect(sig.methodName, type, {
-      fn: effectFn, info: info
-    });
-  }
-}
-
-/**
- * Calls a method with arguments marshaled from properties on the instance
- * based on the method signature contained in the effect metadata.
- *
- * Multi-property observers, computed properties, and inline computing
- * functions call this function to invoke the method, then use the return
- * value accordingly.
- *
- * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
- * @param {string} property Name of property
- * @param {Object} props Bag of current property changes
- * @param {Object} oldProps Bag of previous values for changed properties
- * @param {?} info Effect metadata
- * @return {*} Returns the return value from the method invocation
- * @private
- */
-function runMethodEffect(inst, property, props, oldProps, info) {
-  // Instances can optionally have a _methodHost which allows redirecting where
-  // to find methods. Currently used by `templatize`.
-  let context = inst._methodHost || inst;
-  let fn = context[info.methodName];
-  if (fn) {
-    let args = inst._marshalArgs(info.args, property, props);
-    return fn.apply(context, args);
-  } else if (!info.dynamicFn) {
-    console.warn('method `' + info.methodName + '` not defined');
-  }
-}
-
-const emptyArray = [];
-
-// Regular expressions used for binding
-const IDENT  = '(?:' + '[a-zA-Z_$][\\w.:$\\-*]*' + ')';
-const NUMBER = '(?:' + '[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?' + ')';
-const SQUOTE_STRING = '(?:' + '\'(?:[^\'\\\\]|\\\\.)*\'' + ')';
-const DQUOTE_STRING = '(?:' + '"(?:[^"\\\\]|\\\\.)*"' + ')';
-const STRING = '(?:' + SQUOTE_STRING + '|' + DQUOTE_STRING + ')';
-const ARGUMENT = '(?:(' + IDENT + '|' + NUMBER + '|' +  STRING + ')\\s*' + ')';
-const ARGUMENTS = '(?:' + ARGUMENT + '(?:,\\s*' + ARGUMENT + ')*' + ')';
-const ARGUMENT_LIST = '(?:' + '\\(\\s*' +
-                              '(?:' + ARGUMENTS + '?' + ')' +
-                            '\\)\\s*' + ')';
-const BINDING = '(' + IDENT + '\\s*' + ARGUMENT_LIST + '?' + ')'; // Group 3
-const OPEN_BRACKET = '(\\[\\[|{{)' + '\\s*';
-const CLOSE_BRACKET = '(?:]]|}})';
-const NEGATE = '(?:(!)\\s*)?'; // Group 2
-const EXPRESSION = OPEN_BRACKET + NEGATE + BINDING + CLOSE_BRACKET;
-const bindingRegex = new RegExp(EXPRESSION, "g");
-
-/**
- * Create a string from binding parts of all the literal parts
- *
- * @param {!Array<BindingPart>} parts All parts to stringify
- * @return {string} String made from the literal parts
- */
-function literalFromParts(parts) {
-  let s = '';
-  for (let i=0; i<parts.length; i++) {
-    let literal = parts[i].literal;
-    s += literal || '';
-  }
-  return s;
-}
-
-/**
- * Parses an expression string for a method signature, and returns a metadata
- * describing the method in terms of `methodName`, `static` (whether all the
- * arguments are literals), and an array of `args`
- *
- * @param {string} expression The expression to parse
- * @return {?MethodSignature} The method metadata object if a method expression was
- *   found, otherwise `undefined`
- * @private
- */
-function parseMethod(expression) {
-  // tries to match valid javascript property names
-  let m = expression.match(/([^\s]+?)\(([\s\S]*)\)/);
-  if (m) {
-    let methodName = m[1];
-    let sig = { methodName, static: true, args: emptyArray };
-    if (m[2].trim()) {
-      // replace escaped commas with comma entity, split on un-escaped commas
-      let args = m[2].replace(/\\,/g, '&comma;').split(',');
-      return parseArgs(args, sig);
-    } else {
-      return sig;
-    }
-  }
-  return null;
-}
-
-/**
- * Parses an array of arguments and sets the `args` property of the supplied
- * signature metadata object. Sets the `static` property to false if any
- * argument is a non-literal.
- *
- * @param {!Array<string>} argList Array of argument names
- * @param {!MethodSignature} sig Method signature metadata object
- * @return {!MethodSignature} The updated signature metadata object
- * @private
- */
-function parseArgs(argList, sig) {
-  sig.args = argList.map(function(rawArg) {
-    let arg = parseArg(rawArg);
-    if (!arg.literal) {
-      sig.static = false;
-    }
-    return arg;
-  }, this);
-  return sig;
-}
-
-/**
- * Parses an individual argument, and returns an argument metadata object
- * with the following fields:
- *
- *   {
- *     value: 'prop',        // property/path or literal value
- *     literal: false,       // whether argument is a literal
- *     structured: false,    // whether the property is a path
- *     rootProperty: 'prop', // the root property of the path
- *     wildcard: false       // whether the argument was a wildcard '.*' path
- *   }
- *
- * @param {string} rawArg The string value of the argument
- * @return {!MethodArg} Argument metadata object
- * @private
- */
-function parseArg(rawArg) {
-  // clean up whitespace
-  let arg = rawArg.trim()
-    // replace comma entity with comma
-    .replace(/&comma;/g, ',')
-    // repair extra escape sequences; note only commas strictly need
-    // escaping, but we allow any other char to be escaped since its
-    // likely users will do this
-    .replace(/\\(.)/g, '\$1')
-    ;
-  // basic argument descriptor
-  let a = {
-    name: arg,
-    value: '',
-    literal: false
-  };
-  // detect literal value (must be String or Number)
-  let fc = arg[0];
-  if (fc === '-') {
-    fc = arg[1];
-  }
-  if (fc >= '0' && fc <= '9') {
-    fc = '#';
-  }
-  switch(fc) {
-    case "'":
-    case '"':
-      a.value = arg.slice(1, -1);
-      a.literal = true;
-      break;
-    case '#':
-      a.value = Number(arg);
-      a.literal = true;
-      break;
-  }
-  // if not literal, look for structured path
-  if (!a.literal) {
-    a.rootProperty = root(arg);
-    // detect structured path (has dots)
-    a.structured = isPath(arg);
-    if (a.structured) {
-      a.wildcard = (arg.slice(-2) == '.*');
-      if (a.wildcard) {
-        a.name = arg.slice(0, -2);
-      }
-    }
-  }
-  return a;
-}
-
-function getArgValue(data, props, path) {
-  let value = get(data, path);
-  // when data is not stored e.g. `splices`, get the value from changedProps
-  // TODO(kschaaf): Note, this can cause a rare issue where the wildcard
-  // info.value could pull a stale value out of changedProps during a reentrant
-  // change that sets the value back to undefined.
-  // https://github.com/Polymer/polymer/issues/5479
-  if (value === undefined) {
-    value = props[path];
-  }
-  return value;
-}
-
-// data api
-
-/**
- * Sends array splice notifications (`.splices` and `.length`)
- *
- * Note: this implementation only accepts normalized paths
- *
- * @param {!Polymer_PropertyEffects} inst Instance to send notifications to
- * @param {Array} array The array the mutations occurred on
- * @param {string} path The path to the array that was mutated
- * @param {Array} splices Array of splice records
- * @return {void}
- * @private
- */
-function notifySplices(inst, array, path, splices) {
-  inst.notifyPath(path + '.splices', { indexSplices: splices });
-  inst.notifyPath(path + '.length', array.length);
-}
-
-/**
- * Creates a splice record and sends an array splice notification for
- * the described mutation
- *
- * Note: this implementation only accepts normalized paths
- *
- * @param {!Polymer_PropertyEffects} inst Instance to send notifications to
- * @param {Array} array The array the mutations occurred on
- * @param {string} path The path to the array that was mutated
- * @param {number} index Index at which the array mutation occurred
- * @param {number} addedCount Number of added items
- * @param {Array} removed Array of removed items
- * @return {void}
- * @private
- */
-function notifySplice(inst, array, path, index, addedCount, removed) {
-  notifySplices(inst, array, path, [{
-    index: index,
-    addedCount: addedCount,
-    removed: removed,
-    object: array,
-    type: 'splice'
-  }]);
-}
-
-/**
- * Returns an upper-cased version of the string.
- *
- * @param {string} name String to uppercase
- * @return {string} Uppercased string
- * @private
- */
-function upper(name) {
-  return name[0].toUpperCase() + name.substring(1);
-}
-
-/**
- * Element class mixin that provides meta-programming for Polymer's template
- * binding and data observation (collectively, "property effects") system.
- *
- * This mixin uses provides the following key static methods for adding
- * property effects to an element class:
- * - `addPropertyEffect`
- * - `createPropertyObserver`
- * - `createMethodObserver`
- * - `createNotifyingProperty`
- * - `createReadOnlyProperty`
- * - `createReflectedProperty`
- * - `createComputedProperty`
- * - `bindTemplate`
- *
- * Each method creates one or more property accessors, along with metadata
- * used by this mixin's implementation of `_propertiesChanged` to perform
- * the property effects.
- *
- * Underscored versions of the above methods also exist on the element
- * prototype for adding property effects on instances at runtime.
- *
- * Note that this mixin overrides several `PropertyAccessors` methods, in
- * many cases to maintain guarantees provided by the Polymer 1.x features;
- * notably it changes property accessors to be synchronous by default
- * whereas the default when using `PropertyAccessors` standalone is to be
- * async by default.
- *
- * @mixinFunction
- * @polymer
- * @appliesMixin TemplateStamp
- * @appliesMixin PropertyAccessors
- * @summary Element class mixin that provides meta-programming for Polymer's
- * template binding and data observation system.
- */
-export const PropertyEffects = dedupingMixin(superClass => {
-
-  /**
-   * @constructor
-   * @implements {Polymer_PropertyAccessors}
-   * @implements {Polymer_TemplateStamp}
-   * @unrestricted
-   * @private
-   */
-  const propertyEffectsBase = TemplateStamp(PropertyAccessors(superClass));
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_PropertyEffects}
-   * @extends {propertyEffectsBase}
-   * @unrestricted
-   */
-  class PropertyEffects extends propertyEffectsBase {
-
-    constructor() {
-      super();
-      /** @type {boolean} */
-      // Used to identify users of this mixin, ala instanceof
-      this.__isPropertyEffectsClient = true;
-      /** @type {number} */
-      // NOTE: used to track re-entrant calls to `_flushProperties`
-      // path changes dirty check against `__dataTemp` only during one "turn"
-      // and are cleared when `__dataCounter` returns to 0.
-      this.__dataCounter = 0;
-      /** @type {boolean} */
-      this.__dataClientsReady;
-      /** @type {Array} */
-      this.__dataPendingClients;
-      /** @type {Object} */
-      this.__dataToNotify;
-      /** @type {Object} */
-      this.__dataLinkedPaths;
-      /** @type {boolean} */
-      this.__dataHasPaths;
-      /** @type {Object} */
-      this.__dataCompoundStorage;
-      /** @type {Polymer_PropertyEffects} */
-      this.__dataHost;
-      /** @type {!Object} */
-      this.__dataTemp;
-      /** @type {boolean} */
-      this.__dataClientsInitialized;
-      /** @type {!Object} */
-      this.__data;
-      /** @type {!Object|null} */
-      this.__dataPending;
-      /** @type {!Object} */
-      this.__dataOld;
-      /** @type {Object} */
-      this.__computeEffects;
-      /** @type {Object} */
-      this.__reflectEffects;
-      /** @type {Object} */
-      this.__notifyEffects;
-      /** @type {Object} */
-      this.__propagateEffects;
-      /** @type {Object} */
-      this.__observeEffects;
-      /** @type {Object} */
-      this.__readOnly;
-      /** @type {!TemplateInfo} */
-      this.__templateInfo;
-    }
-
-    get PROPERTY_EFFECT_TYPES() {
-      return TYPES;
-    }
-
-    /**
-     * @override
-     * @return {void}
-     */
-    _initializeProperties() {
-      super._initializeProperties();
-      hostStack.registerHost(this);
-      this.__dataClientsReady = false;
-      this.__dataPendingClients = null;
-      this.__dataToNotify = null;
-      this.__dataLinkedPaths = null;
-      this.__dataHasPaths = false;
-      // May be set on instance prior to upgrade
-      this.__dataCompoundStorage = this.__dataCompoundStorage || null;
-      this.__dataHost = this.__dataHost || null;
-      this.__dataTemp = {};
-      this.__dataClientsInitialized = false;
-    }
-
-    /**
-     * Overrides `PropertyAccessors` implementation to provide a
-     * more efficient implementation of initializing properties from
-     * the prototype on the instance.
-     *
-     * @override
-     * @param {Object} props Properties to initialize on the prototype
-     * @return {void}
-     */
-    _initializeProtoProperties(props) {
-      this.__data = Object.create(props);
-      this.__dataPending = Object.create(props);
-      this.__dataOld = {};
-    }
-
-    /**
-     * Overrides `PropertyAccessors` implementation to avoid setting
-     * `_setProperty`'s `shouldNotify: true`.
-     *
-     * @override
-     * @param {Object} props Properties to initialize on the instance
-     * @return {void}
-     */
-    _initializeInstanceProperties(props) {
-      let readOnly = this[TYPES.READ_ONLY];
-      for (let prop in props) {
-        if (!readOnly || !readOnly[prop]) {
-          this.__dataPending = this.__dataPending || {};
-          this.__dataOld = this.__dataOld || {};
-          this.__data[prop] = this.__dataPending[prop] = props[prop];
-        }
-      }
-    }
-
-    // Prototype setup ----------------------------------------
-
-    /**
-     * Equivalent to static `addPropertyEffect` API but can be called on
-     * an instance to add effects at runtime.  See that method for
-     * full API docs.
-     *
-     * @override
-     * @param {string} property Property that should trigger the effect
-     * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
-     * @param {Object=} effect Effect metadata object
-     * @return {void}
-     * @protected
-     */
-    _addPropertyEffect(property, type, effect) {
-      this._createPropertyAccessor(property, type == TYPES.READ_ONLY);
-      // effects are accumulated into arrays per property based on type
-      let effects = ensureOwnEffectMap(this, type)[property];
-      if (!effects) {
-        effects = this[type][property] = [];
-      }
-      effects.push(effect);
-    }
-
-    /**
-     * Removes the given property effect.
-     *
-     * @override
-     * @param {string} property Property the effect was associated with
-     * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
-     * @param {Object=} effect Effect metadata object to remove
-     * @return {void}
-     */
-    _removePropertyEffect(property, type, effect) {
-      let effects = ensureOwnEffectMap(this, type)[property];
-      let idx = effects.indexOf(effect);
-      if (idx >= 0) {
-        effects.splice(idx, 1);
-      }
-    }
-
-    /**
-     * Returns whether the current prototype/instance has a property effect
-     * of a certain type.
-     *
-     * @override
-     * @param {string} property Property name
-     * @param {string=} type Effect type, from this.PROPERTY_EFFECT_TYPES
-     * @return {boolean} True if the prototype/instance has an effect of this
-     *     type
-     * @protected
-     */
-    _hasPropertyEffect(property, type) {
-      let effects = this[type];
-      return Boolean(effects && effects[property]);
-    }
-
-    /**
-     * Returns whether the current prototype/instance has a "read only"
-     * accessor for the given property.
-     *
-     * @override
-     * @param {string} property Property name
-     * @return {boolean} True if the prototype/instance has an effect of this
-     *     type
-     * @protected
-     */
-    _hasReadOnlyEffect(property) {
-      return this._hasPropertyEffect(property, TYPES.READ_ONLY);
-    }
-
-    /**
-     * Returns whether the current prototype/instance has a "notify"
-     * property effect for the given property.
-     *
-     * @override
-     * @param {string} property Property name
-     * @return {boolean} True if the prototype/instance has an effect of this
-     *     type
-     * @protected
-     */
-    _hasNotifyEffect(property) {
-      return this._hasPropertyEffect(property, TYPES.NOTIFY);
-    }
-
-    /**
-     * Returns whether the current prototype/instance has a "reflect to
-     * attribute" property effect for the given property.
-     *
-     * @override
-     * @param {string} property Property name
-     * @return {boolean} True if the prototype/instance has an effect of this
-     *     type
-     * @protected
-     */
-    _hasReflectEffect(property) {
-      return this._hasPropertyEffect(property, TYPES.REFLECT);
-    }
-
-    /**
-     * Returns whether the current prototype/instance has a "computed"
-     * property effect for the given property.
-     *
-     * @override
-     * @param {string} property Property name
-     * @return {boolean} True if the prototype/instance has an effect of this
-     *     type
-     * @protected
-     */
-    _hasComputedEffect(property) {
-      return this._hasPropertyEffect(property, TYPES.COMPUTE);
-    }
-
-    // Runtime ----------------------------------------
-
-    /**
-     * Sets a pending property or path.  If the root property of the path in
-     * question had no accessor, the path is set, otherwise it is enqueued
-     * via `_setPendingProperty`.
-     *
-     * This function isolates relatively expensive functionality necessary
-     * for the public API (`set`, `setProperties`, `notifyPath`, and property
-     * change listeners via {{...}} bindings), such that it is only done
-     * when paths enter the system, and not at every propagation step.  It
-     * also sets a `__dataHasPaths` flag on the instance which is used to
-     * fast-path slower path-matching code in the property effects host paths.
-     *
-     * `path` can be a path string or array of path parts as accepted by the
-     * public API.
-     *
-     * @override
-     * @param {string | !Array<number|string>} path Path to set
-     * @param {*} value Value to set
-     * @param {boolean=} shouldNotify Set to true if this change should
-     *  cause a property notification event dispatch
-     * @param {boolean=} isPathNotification If the path being set is a path
-     *   notification of an already changed value, as opposed to a request
-     *   to set and notify the change.  In the latter `false` case, a dirty
-     *   check is performed and then the value is set to the path before
-     *   enqueuing the pending property change.
-     * @return {boolean} Returns true if the property/path was enqueued in
-     *   the pending changes bag.
-     * @protected
-     */
-    _setPendingPropertyOrPath(path, value, shouldNotify, isPathNotification) {
-      if (isPathNotification ||
-          root(Array.isArray(path) ? path[0] : path) !== path) {
-        // Dirty check changes being set to a path against the actual object,
-        // since this is the entry point for paths into the system; from here
-        // the only dirty checks are against the `__dataTemp` cache to prevent
-        // duplicate work in the same turn only. Note, if this was a notification
-        // of a change already set to a path (isPathNotification: true),
-        // we always let the change through and skip the `set` since it was
-        // already dirty checked at the point of entry and the underlying
-        // object has already been updated
-        if (!isPathNotification) {
-          let old = get(this, path);
-          path = /** @type {string} */ (set(this, path, value));
-          // Use property-accessor's simpler dirty check
-          if (!path || !super._shouldPropertyChange(path, value, old)) {
-            return false;
-          }
-        }
-        this.__dataHasPaths = true;
-        if (this._setPendingProperty(/**@type{string}*/(path), value, shouldNotify)) {
-          computeLinkedPaths(this, /**@type{string}*/ (path), value);
-          return true;
-        }
-      } else {
-        if (this.__dataHasAccessor && this.__dataHasAccessor[path]) {
-          return this._setPendingProperty(/**@type{string}*/(path), value, shouldNotify);
-        } else {
-          this[path] = value;
-        }
-      }
-      return false;
-    }
-
-    /**
-     * Applies a value to a non-Polymer element/node's property.
-     *
-     * The implementation makes a best-effort at binding interop:
-     * Some native element properties have side-effects when
-     * re-setting the same value (e.g. setting `<input>.value` resets the
-     * cursor position), so we do a dirty-check before setting the value.
-     * However, for better interop with non-Polymer custom elements that
-     * accept objects, we explicitly re-set object changes coming from the
-     * Polymer world (which may include deep object changes without the
-     * top reference changing), erring on the side of providing more
-     * information.
-     *
-     * Users may override this method to provide alternate approaches.
-     *
-     * @override
-     * @param {!Node} node The node to set a property on
-     * @param {string} prop The property to set
-     * @param {*} value The value to set
-     * @return {void}
-     * @protected
-     */
-    _setUnmanagedPropertyToNode(node, prop, value) {
-      // It is a judgment call that resetting primitives is
-      // "bad" and resettings objects is also "good"; alternatively we could
-      // implement a whitelist of tag & property values that should never
-      // be reset (e.g. <input>.value && <select>.value)
-      if (value !== node[prop] || typeof value == 'object') {
-        node[prop] = value;
-      }
-    }
-
-    /**
-     * Overrides the `PropertiesChanged` implementation to introduce special
-     * dirty check logic depending on the property & value being set:
-     *
-     * 1. Any value set to a path (e.g. 'obj.prop': 42 or 'obj.prop': {...})
-     *    Stored in `__dataTemp`, dirty checked against `__dataTemp`
-     * 2. Object set to simple property (e.g. 'prop': {...})
-     *    Stored in `__dataTemp` and `__data`, dirty checked against
-     *    `__dataTemp` by default implementation of `_shouldPropertyChange`
-     * 3. Primitive value set to simple property (e.g. 'prop': 42)
-     *    Stored in `__data`, dirty checked against `__data`
-     *
-     * The dirty-check is important to prevent cycles due to two-way
-     * notification, but paths and objects are only dirty checked against any
-     * previous value set during this turn via a "temporary cache" that is
-     * cleared when the last `_propertiesChanged` exits. This is so:
-     * a. any cached array paths (e.g. 'array.3.prop') may be invalidated
-     *    due to array mutations like shift/unshift/splice; this is fine
-     *    since path changes are dirty-checked at user entry points like `set`
-     * b. dirty-checking for objects only lasts one turn to allow the user
-     *    to mutate the object in-place and re-set it with the same identity
-     *    and have all sub-properties re-propagated in a subsequent turn.
-     *
-     * The temp cache is not necessarily sufficient to prevent invalid array
-     * paths, since a splice can happen during the same turn (with pathological
-     * user code); we could introduce a "fixup" for temporarily cached array
-     * paths if needed: https://github.com/Polymer/polymer/issues/4227
-     *
-     * @override
-     * @param {string} property Name of the property
-     * @param {*} value Value to set
-     * @param {boolean=} shouldNotify True if property should fire notification
-     *   event (applies only for `notify: true` properties)
-     * @return {boolean} Returns true if the property changed
-     */
-    _setPendingProperty(property, value, shouldNotify) {
-      let propIsPath = this.__dataHasPaths && isPath(property);
-      let prevProps = propIsPath ? this.__dataTemp : this.__data;
-      if (this._shouldPropertyChange(property, value, prevProps[property])) {
-        if (!this.__dataPending) {
-          this.__dataPending = {};
-          this.__dataOld = {};
-        }
-        // Ensure old is captured from the last turn
-        if (!(property in this.__dataOld)) {
-          this.__dataOld[property] = this.__data[property];
-        }
-        // Paths are stored in temporary cache (cleared at end of turn),
-        // which is used for dirty-checking, all others stored in __data
-        if (propIsPath) {
-          this.__dataTemp[property] = value;
-        } else {
-          this.__data[property] = value;
-        }
-        // All changes go into pending property bag, passed to _propertiesChanged
-        this.__dataPending[property] = value;
-        // Track properties that should notify separately
-        if (propIsPath || (this[TYPES.NOTIFY] && this[TYPES.NOTIFY][property])) {
-          this.__dataToNotify = this.__dataToNotify || {};
-          this.__dataToNotify[property] = shouldNotify;
-        }
-        return true;
-      }
-      return false;
-    }
-
-    /**
-     * Overrides base implementation to ensure all accessors set `shouldNotify`
-     * to true, for per-property notification tracking.
-     *
-     * @override
-     * @param {string} property Name of the property
-     * @param {*} value Value to set
-     * @return {void}
-     */
-    _setProperty(property, value) {
-      if (this._setPendingProperty(property, value, true)) {
-        this._invalidateProperties();
-      }
-    }
-
-    /**
-     * Overrides `PropertyAccessor`'s default async queuing of
-     * `_propertiesChanged`: if `__dataReady` is false (has not yet been
-     * manually flushed), the function no-ops; otherwise flushes
-     * `_propertiesChanged` synchronously.
-     *
-     * @override
-     * @return {void}
-     */
-    _invalidateProperties() {
-      if (this.__dataReady) {
-        this._flushProperties();
-      }
-    }
-
-    /**
-     * Enqueues the given client on a list of pending clients, whose
-     * pending property changes can later be flushed via a call to
-     * `_flushClients`.
-     *
-     * @override
-     * @param {Object} client PropertyEffects client to enqueue
-     * @return {void}
-     * @protected
-     */
-    _enqueueClient(client) {
-      this.__dataPendingClients = this.__dataPendingClients || [];
-      if (client !== this) {
-        this.__dataPendingClients.push(client);
-      }
-    }
-
-    /**
-     * Overrides superclass implementation.
-     *
-     * @override
-     * @return {void}
-     * @protected
-     */
-    _flushProperties() {
-      this.__dataCounter++;
-      super._flushProperties();
-      this.__dataCounter--;
-    }
-
-    /**
-     * Flushes any clients previously enqueued via `_enqueueClient`, causing
-     * their `_flushProperties` method to run.
-     *
-     * @override
-     * @return {void}
-     * @protected
-     */
-    _flushClients() {
-      if (!this.__dataClientsReady) {
-        this.__dataClientsReady = true;
-        this._readyClients();
-        // Override point where accessors are turned on; importantly,
-        // this is after clients have fully readied, providing a guarantee
-        // that any property effects occur only after all clients are ready.
-        this.__dataReady = true;
-      } else {
-        this.__enableOrFlushClients();
-      }
-    }
-
-    // NOTE: We ensure clients either enable or flush as appropriate. This
-    // handles two corner cases:
-    // (1) clients flush properly when connected/enabled before the host
-    // enables; e.g.
-    //   (a) Templatize stamps with no properties and does not flush and
-    //   (b) the instance is inserted into dom and
-    //   (c) then the instance flushes.
-    // (2) clients enable properly when not connected/enabled when the host
-    // flushes; e.g.
-    //   (a) a template is runtime stamped and not yet connected/enabled
-    //   (b) a host sets a property, causing stamped dom to flush
-    //   (c) the stamped dom enables.
-    __enableOrFlushClients() {
-      let clients = this.__dataPendingClients;
-      if (clients) {
-        this.__dataPendingClients = null;
-        for (let i=0; i < clients.length; i++) {
-          let client = clients[i];
-          if (!client.__dataEnabled) {
-            client._enableProperties();
-          } else if (client.__dataPending) {
-            client._flushProperties();
-          }
-        }
-      }
-    }
-
-    /**
-     * Perform any initial setup on client dom. Called before the first
-     * `_flushProperties` call on client dom and before any element
-     * observers are called.
-     *
-     * @override
-     * @return {void}
-     * @protected
-     */
-    _readyClients() {
-      this.__enableOrFlushClients();
-    }
-
-    /**
-     * Sets a bag of property changes to this instance, and
-     * synchronously processes all effects of the properties as a batch.
-     *
-     * Property names must be simple properties, not paths.  Batched
-     * path propagation is not supported.
-     *
-     * @override
-     * @param {Object} props Bag of one or more key-value pairs whose key is
-     *   a property and value is the new value to set for that property.
-     * @param {boolean=} setReadOnly When true, any private values set in
-     *   `props` will be set. By default, `setProperties` will not set
-     *   `readOnly: true` root properties.
-     * @return {void}
-     * @public
-     */
-    setProperties(props, setReadOnly) {
-      for (let path in props) {
-        if (setReadOnly || !this[TYPES.READ_ONLY] || !this[TYPES.READ_ONLY][path]) {
-          //TODO(kschaaf): explicitly disallow paths in setProperty?
-          // wildcard observers currently only pass the first changed path
-          // in the `info` object, and you could do some odd things batching
-          // paths, e.g. {'foo.bar': {...}, 'foo': null}
-          this._setPendingPropertyOrPath(path, props[path], true);
-        }
-      }
-      this._invalidateProperties();
-    }
-
-    /**
-     * Overrides `PropertyAccessors` so that property accessor
-     * side effects are not enabled until after client dom is fully ready.
-     * Also calls `_flushClients` callback to ensure client dom is enabled
-     * that was not enabled as a result of flushing properties.
-     *
-     * @override
-     * @return {void}
-     */
-    ready() {
-      // It is important that `super.ready()` is not called here as it
-      // immediately turns on accessors. Instead, we wait until `readyClients`
-      // to enable accessors to provide a guarantee that clients are ready
-      // before processing any accessors side effects.
-      this._flushProperties();
-      // If no data was pending, `_flushProperties` will not `flushClients`
-      // so ensure this is done.
-      if (!this.__dataClientsReady) {
-        this._flushClients();
-      }
-      // Before ready, client notifications do not trigger _flushProperties.
-      // Therefore a flush is necessary here if data has been set.
-      if (this.__dataPending) {
-        this._flushProperties();
-      }
-    }
-
-    /**
-     * Implements `PropertyAccessors`'s properties changed callback.
-     *
-     * Runs each class of effects for the batch of changed properties in
-     * a specific order (compute, propagate, reflect, observe, notify).
-     *
-     * @override
-     * @param {!Object} currentProps Bag of all current accessor values
-     * @param {?Object} changedProps Bag of properties changed since the last
-     *   call to `_propertiesChanged`
-     * @param {?Object} oldProps Bag of previous values for each property
-     *   in `changedProps`
-     * @return {void}
-     */
-    _propertiesChanged(currentProps, changedProps, oldProps) {
-      // ----------------------------
-      // let c = Object.getOwnPropertyNames(changedProps || {});
-      // window.debug && console.group(this.localName + '#' + this.id + ': ' + c);
-      // if (window.debug) { debugger; }
-      // ----------------------------
-      let hasPaths = this.__dataHasPaths;
-      this.__dataHasPaths = false;
-      // Compute properties
-      runComputedEffects(this, changedProps, oldProps, hasPaths);
-      // Clear notify properties prior to possible reentry (propagate, observe),
-      // but after computing effects have a chance to add to them
-      let notifyProps = this.__dataToNotify;
-      this.__dataToNotify = null;
-      // Propagate properties to clients
-      this._propagatePropertyChanges(changedProps, oldProps, hasPaths);
-      // Flush clients
-      this._flushClients();
-      // Reflect properties
-      runEffects(this, this[TYPES.REFLECT], changedProps, oldProps, hasPaths);
-      // Observe properties
-      runEffects(this, this[TYPES.OBSERVE], changedProps, oldProps, hasPaths);
-      // Notify properties to host
-      if (notifyProps) {
-        runNotifyEffects(this, notifyProps, changedProps, oldProps, hasPaths);
-      }
-      // Clear temporary cache at end of turn
-      if (this.__dataCounter == 1) {
-        this.__dataTemp = {};
-      }
-      // ----------------------------
-      // window.debug && console.groupEnd(this.localName + '#' + this.id + ': ' + c);
-      // ----------------------------
-    }
-
-    /**
-     * Called to propagate any property changes to stamped template nodes
-     * managed by this element.
-     *
-     * @override
-     * @param {Object} changedProps Bag of changed properties
-     * @param {Object} oldProps Bag of previous values for changed properties
-     * @param {boolean} hasPaths True with `props` contains one or more paths
-     * @return {void}
-     * @protected
-     */
-    _propagatePropertyChanges(changedProps, oldProps, hasPaths) {
-      if (this[TYPES.PROPAGATE]) {
-        runEffects(this, this[TYPES.PROPAGATE], changedProps, oldProps, hasPaths);
-      }
-      let templateInfo = this.__templateInfo;
-      while (templateInfo) {
-        runEffects(this, templateInfo.propertyEffects, changedProps, oldProps,
-          hasPaths, templateInfo.nodeList);
-        templateInfo = templateInfo.nextTemplateInfo;
-      }
-    }
-
-    /**
-     * Aliases one data path as another, such that path notifications from one
-     * are routed to the other.
-     *
-     * @override
-     * @param {string | !Array<string|number>} to Target path to link.
-     * @param {string | !Array<string|number>} from Source path to link.
-     * @return {void}
-     * @public
-     */
-    linkPaths(to, from) {
-      to = normalize(to);
-      from = normalize(from);
-      this.__dataLinkedPaths = this.__dataLinkedPaths || {};
-      this.__dataLinkedPaths[to] = from;
-    }
-
-    /**
-     * Removes a data path alias previously established with `_linkPaths`.
-     *
-     * Note, the path to unlink should be the target (`to`) used when
-     * linking the paths.
-     *
-     * @override
-     * @param {string | !Array<string|number>} path Target path to unlink.
-     * @return {void}
-     * @public
-     */
-    unlinkPaths(path) {
-      path = normalize(path);
-      if (this.__dataLinkedPaths) {
-        delete this.__dataLinkedPaths[path];
-      }
-    }
-
-    /**
-     * Notify that an array has changed.
-     *
-     * Example:
-     *
-     *     this.items = [ {name: 'Jim'}, {name: 'Todd'}, {name: 'Bill'} ];
-     *     ...
-     *     this.items.splice(1, 1, {name: 'Sam'});
-     *     this.items.push({name: 'Bob'});
-     *     this.notifySplices('items', [
-     *       { index: 1, removed: [{name: 'Todd'}], addedCount: 1,
-     *         object: this.items, type: 'splice' },
-     *       { index: 3, removed: [], addedCount: 1,
-     *         object: this.items, type: 'splice'}
-     *     ]);
-     *
-     * @param {string} path Path that should be notified.
-     * @param {Array} splices Array of splice records indicating ordered
-     *   changes that occurred to the array. Each record should have the
-     *   following fields:
-     *    * index: index at which the change occurred
-     *    * removed: array of items that were removed from this index
-     *    * addedCount: number of new items added at this index
-     *    * object: a reference to the array in question
-     *    * type: the string literal 'splice'
-     *
-     *   Note that splice records _must_ be normalized such that they are
-     *   reported in index order (raw results from `Object.observe` are not
-     *   ordered and must be normalized/merged before notifying).
-     *
-     * @override
-     * @return {void}
-     * @public
-     */
-    notifySplices(path, splices) {
-      let info = {path: ''};
-      let array = /** @type {Array} */(get(this, path, info));
-      notifySplices(this, array, info.path, splices);
-    }
-
-    /**
-     * Convenience method for reading a value from a path.
-     *
-     * Note, if any part in the path is undefined, this method returns
-     * `undefined` (this method does not throw when dereferencing undefined
-     * paths).
-     *
-     * @override
-     * @param {(string|!Array<(string|number)>)} path Path to the value
-     *   to read.  The path may be specified as a string (e.g. `foo.bar.baz`)
-     *   or an array of path parts (e.g. `['foo.bar', 'baz']`).  Note that
-     *   bracketed expressions are not supported; string-based path parts
-     *   *must* be separated by dots.  Note that when dereferencing array
-     *   indices, the index may be used as a dotted part directly
-     *   (e.g. `users.12.name` or `['users', 12, 'name']`).
-     * @param {Object=} root Root object from which the path is evaluated.
-     * @return {*} Value at the path, or `undefined` if any part of the path
-     *   is undefined.
-     * @public
-     */
-    get(path, root) {
-      return get(root || this, path);
-    }
-
-    /**
-     * Convenience method for setting a value to a path and notifying any
-     * elements bound to the same path.
-     *
-     * Note, if any part in the path except for the last is undefined,
-     * this method does nothing (this method does not throw when
-     * dereferencing undefined paths).
-     *
-     * @override
-     * @param {(string|!Array<(string|number)>)} path Path to the value
-     *   to write.  The path may be specified as a string (e.g. `'foo.bar.baz'`)
-     *   or an array of path parts (e.g. `['foo.bar', 'baz']`).  Note that
-     *   bracketed expressions are not supported; string-based path parts
-     *   *must* be separated by dots.  Note that when dereferencing array
-     *   indices, the index may be used as a dotted part directly
-     *   (e.g. `'users.12.name'` or `['users', 12, 'name']`).
-     * @param {*} value Value to set at the specified path.
-     * @param {Object=} root Root object from which the path is evaluated.
-     *   When specified, no notification will occur.
-     * @return {void}
-     * @public
-     */
-    set(path, value, root) {
-      if (root) {
-        set(root, path, value);
-      } else {
-        if (!this[TYPES.READ_ONLY] || !this[TYPES.READ_ONLY][/** @type {string} */(path)]) {
-          if (this._setPendingPropertyOrPath(path, value, true)) {
-            this._invalidateProperties();
-          }
-        }
-      }
-    }
-
-    /**
-     * Adds items onto the end of the array at the path specified.
-     *
-     * The arguments after `path` and return value match that of
-     * `Array.prototype.push`.
-     *
-     * This method notifies other paths to the same array that a
-     * splice occurred to the array.
-     *
-     * @override
-     * @param {string | !Array<string|number>} path Path to array.
-     * @param {...*} items Items to push onto array
-     * @return {number} New length of the array.
-     * @public
-     */
-    push(path, ...items) {
-      let info = {path: ''};
-      let array = /** @type {Array}*/(get(this, path, info));
-      let len = array.length;
-      let ret = array.push(...items);
-      if (items.length) {
-        notifySplice(this, array, info.path, len, items.length, []);
-      }
-      return ret;
-    }
-
-    /**
-     * Removes an item from the end of array at the path specified.
-     *
-     * The arguments after `path` and return value match that of
-     * `Array.prototype.pop`.
-     *
-     * This method notifies other paths to the same array that a
-     * splice occurred to the array.
-     *
-     * @override
-     * @param {string | !Array<string|number>} path Path to array.
-     * @return {*} Item that was removed.
-     * @public
-     */
-    pop(path) {
-      let info = {path: ''};
-      let array = /** @type {Array} */(get(this, path, info));
-      let hadLength = Boolean(array.length);
-      let ret = array.pop();
-      if (hadLength) {
-        notifySplice(this, array, info.path, array.length, 0, [ret]);
-      }
-      return ret;
-    }
-
-    /**
-     * Starting from the start index specified, removes 0 or more items
-     * from the array and inserts 0 or more new items in their place.
-     *
-     * The arguments after `path` and return value match that of
-     * `Array.prototype.splice`.
-     *
-     * This method notifies other paths to the same array that a
-     * splice occurred to the array.
-     *
-     * @override
-     * @param {string | !Array<string|number>} path Path to array.
-     * @param {number} start Index from which to start removing/inserting.
-     * @param {number=} deleteCount Number of items to remove.
-     * @param {...*} items Items to insert into array.
-     * @return {Array} Array of removed items.
-     * @public
-     */
-    splice(path, start, deleteCount, ...items) {
-      let info = {path : ''};
-      let array = /** @type {Array} */(get(this, path, info));
-      // Normalize fancy native splice handling of crazy start values
-      if (start < 0) {
-        start = array.length - Math.floor(-start);
-      } else if (start) {
-        start = Math.floor(start);
-      }
-      // array.splice does different things based on the number of arguments
-      // you pass in. Therefore, array.splice(0) and array.splice(0, undefined)
-      // do different things. In the former, the whole array is cleared. In the
-      // latter, no items are removed.
-      // This means that we need to detect whether 1. one of the arguments
-      // is actually passed in and then 2. determine how many arguments
-      // we should pass on to the native array.splice
-      //
-      let ret;
-      // Omit any additional arguments if they were not passed in
-      if (arguments.length === 2) {
-        ret = array.splice(start);
-      // Either start was undefined and the others were defined, but in this
-      // case we can safely pass on all arguments
-      //
-      // Note: this includes the case where none of the arguments were passed in,
-      // e.g. this.splice('array'). However, if both start and deleteCount
-      // are undefined, array.splice will not modify the array (as expected)
-      } else {
-        ret = array.splice(start, deleteCount, ...items);
-      }
-      // At the end, check whether any items were passed in (e.g. insertions)
-      // or if the return array contains items (e.g. deletions).
-      // Only notify if items were added or deleted.
-      if (items.length || ret.length) {
-        notifySplice(this, array, info.path, start, items.length, ret);
-      }
-      return ret;
-    }
-
-    /**
-     * Removes an item from the beginning of array at the path specified.
-     *
-     * The arguments after `path` and return value match that of
-     * `Array.prototype.pop`.
-     *
-     * This method notifies other paths to the same array that a
-     * splice occurred to the array.
-     *
-     * @override
-     * @param {string | !Array<string|number>} path Path to array.
-     * @return {*} Item that was removed.
-     * @public
-     */
-    shift(path) {
-      let info = {path: ''};
-      let array = /** @type {Array} */(get(this, path, info));
-      let hadLength = Boolean(array.length);
-      let ret = array.shift();
-      if (hadLength) {
-        notifySplice(this, array, info.path, 0, 0, [ret]);
-      }
-      return ret;
-    }
-
-    /**
-     * Adds items onto the beginning of the array at the path specified.
-     *
-     * The arguments after `path` and return value match that of
-     * `Array.prototype.push`.
-     *
-     * This method notifies other paths to the same array that a
-     * splice occurred to the array.
-     *
-     * @override
-     * @param {string | !Array<string|number>} path Path to array.
-     * @param {...*} items Items to insert info array
-     * @return {number} New length of the array.
-     * @public
-     */
-    unshift(path, ...items) {
-      let info = {path: ''};
-      let array = /** @type {Array} */(get(this, path, info));
-      let ret = array.unshift(...items);
-      if (items.length) {
-        notifySplice(this, array, info.path, 0, items.length, []);
-      }
-      return ret;
-    }
-
-    /**
-     * Notify that a path has changed.
-     *
-     * Example:
-     *
-     *     this.item.user.name = 'Bob';
-     *     this.notifyPath('item.user.name');
-     *
-     * @override
-     * @param {string} path Path that should be notified.
-     * @param {*=} value Value at the path (optional).
-     * @return {void}
-     * @public
-     */
-    notifyPath(path, value) {
-      /** @type {string} */
-      let propPath;
-      if (arguments.length == 1) {
-        // Get value if not supplied
-        let info = {path: ''};
-        value = get(this, path, info);
-        propPath = info.path;
-      } else if (Array.isArray(path)) {
-        // Normalize path if needed
-        propPath = normalize(path);
-      } else {
-        propPath = /** @type{string} */(path);
-      }
-      if (this._setPendingPropertyOrPath(propPath, value, true, true)) {
-        this._invalidateProperties();
-      }
-    }
-
-    /**
-     * Equivalent to static `createReadOnlyProperty` API but can be called on
-     * an instance to add effects at runtime.  See that method for
-     * full API docs.
-     *
-     * @override
-     * @param {string} property Property name
-     * @param {boolean=} protectedSetter Creates a custom protected setter
-     *   when `true`.
-     * @return {void}
-     * @protected
-     */
-    _createReadOnlyProperty(property, protectedSetter) {
-      this._addPropertyEffect(property, TYPES.READ_ONLY);
-      if (protectedSetter) {
-        this['_set' + upper(property)] = /** @this {PropertyEffects} */function(value) {
-          this._setProperty(property, value);
-        };
-      }
-    }
-
-    /**
-     * Equivalent to static `createPropertyObserver` API but can be called on
-     * an instance to add effects at runtime.  See that method for
-     * full API docs.
-     *
-     * @override
-     * @param {string} property Property name
-     * @param {string|function(*,*)} method Function or name of observer method
-     *     to call
-     * @param {boolean=} dynamicFn Whether the method name should be included as
-     *   a dependency to the effect.
-     * @return {void}
-     * @protected
-     */
-    _createPropertyObserver(property, method, dynamicFn) {
-      let info = { property, method, dynamicFn: Boolean(dynamicFn) };
-      this._addPropertyEffect(property, TYPES.OBSERVE, {
-        fn: runObserverEffect, info, trigger: {name: property}
-      });
-      if (dynamicFn) {
-        this._addPropertyEffect(/** @type {string} */(method), TYPES.OBSERVE, {
-          fn: runObserverEffect, info, trigger: {name: method}
-        });
-      }
-    }
-
-    /**
-     * Equivalent to static `createMethodObserver` API but can be called on
-     * an instance to add effects at runtime.  See that method for
-     * full API docs.
-     *
-     * @override
-     * @param {string} expression Method expression
-     * @param {boolean|Object=} dynamicFn Boolean or object map indicating
-     *   whether method names should be included as a dependency to the effect.
-     * @return {void}
-     * @protected
-     */
-    _createMethodObserver(expression, dynamicFn) {
-      let sig = parseMethod(expression);
-      if (!sig) {
-        throw new Error("Malformed observer expression '" + expression + "'");
-      }
-      createMethodEffect(this, sig, TYPES.OBSERVE, runMethodEffect, null, dynamicFn);
-    }
-
-    /**
-     * Equivalent to static `createNotifyingProperty` API but can be called on
-     * an instance to add effects at runtime.  See that method for
-     * full API docs.
-     *
-     * @override
-     * @param {string} property Property name
-     * @return {void}
-     * @protected
-     */
-    _createNotifyingProperty(property) {
-      this._addPropertyEffect(property, TYPES.NOTIFY, {
-        fn: runNotifyEffect,
-        info: {
-          eventName: camelToDashCase(property) + '-changed',
-          property: property
-        }
-      });
-    }
-
-    /**
-     * Equivalent to static `createReflectedProperty` API but can be called on
-     * an instance to add effects at runtime.  See that method for
-     * full API docs.
-     *
-     * @override
-     * @param {string} property Property name
-     * @return {void}
-     * @protected
-     * @suppress {missingProperties} go/missingfnprops
-     */
-    _createReflectedProperty(property) {
-      let attr = this.constructor.attributeNameForProperty(property);
-      if (attr[0] === '-') {
-        console.warn('Property ' + property + ' cannot be reflected to attribute ' +
-          attr + ' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.');
-      } else {
-        this._addPropertyEffect(property, TYPES.REFLECT, {
-          fn: runReflectEffect,
-          info: {
-            attrName: attr
-          }
-        });
-      }
-    }
-
-    /**
-     * Equivalent to static `createComputedProperty` API but can be called on
-     * an instance to add effects at runtime.  See that method for
-     * full API docs.
-     *
-     * @override
-     * @param {string} property Name of computed property to set
-     * @param {string} expression Method expression
-     * @param {boolean|Object=} dynamicFn Boolean or object map indicating
-     *   whether method names should be included as a dependency to the effect.
-     * @return {void}
-     * @protected
-     */
-    _createComputedProperty(property, expression, dynamicFn) {
-      let sig = parseMethod(expression);
-      if (!sig) {
-        throw new Error("Malformed computed expression '" + expression + "'");
-      }
-      createMethodEffect(this, sig, TYPES.COMPUTE, runComputedEffect, property, dynamicFn);
-    }
-
-    /**
-     * Gather the argument values for a method specified in the provided array
-     * of argument metadata.
-     *
-     * The `path` and `value` arguments are used to fill in wildcard descriptor
-     * when the method is being called as a result of a path notification.
-     *
-     * @param {!Array<!MethodArg>} args Array of argument metadata
-     * @param {string} path Property/path name that triggered the method effect
-     * @param {Object} props Bag of current property changes
-     * @return {Array<*>} Array of argument values
-     * @private
-     */
-    _marshalArgs(args, path, props) {
-      const data = this.__data;
-      const values = [];
-      for (let i=0, l=args.length; i<l; i++) {
-        let {name, structured, wildcard, value, literal} = args[i];
-        if (!literal) {
-          if (wildcard) {
-            const matches = isDescendant(name, path);
-            const pathValue = getArgValue(data, props, matches ? path : name);
-            value = {
-              path: matches ? path : name,
-              value: pathValue,
-              base: matches ? get(data, name) : pathValue
-            };
-          } else {
-            value = structured ? getArgValue(data, props, name) : data[name];
-          }
-        }
-        values[i] = value;
-      }
-      return values;
-    }
-
-    // -- static class methods ------------
-
-    /**
-     * Ensures an accessor exists for the specified property, and adds
-     * to a list of "property effects" that will run when the accessor for
-     * the specified property is set.  Effects are grouped by "type", which
-     * roughly corresponds to a phase in effect processing.  The effect
-     * metadata should be in the following form:
-     *
-     *     {
-     *       fn: effectFunction, // Reference to function to call to perform effect
-     *       info: { ... }       // Effect metadata passed to function
-     *       trigger: {          // Optional triggering metadata; if not provided
-     *         name: string      // the property is treated as a wildcard
-     *         structured: boolean
-     *         wildcard: boolean
-     *       }
-     *     }
-     *
-     * Effects are called from `_propertiesChanged` in the following order by
-     * type:
-     *
-     * 1. COMPUTE
-     * 2. PROPAGATE
-     * 3. REFLECT
-     * 4. OBSERVE
-     * 5. NOTIFY
-     *
-     * Effect functions are called with the following signature:
-     *
-     *     effectFunction(inst, path, props, oldProps, info, hasPaths)
-     *
-     * @param {string} property Property that should trigger the effect
-     * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
-     * @param {Object=} effect Effect metadata object
-     * @return {void}
-     * @protected
-     */
-    static addPropertyEffect(property, type, effect) {
-      this.prototype._addPropertyEffect(property, type, effect);
-    }
-
-    /**
-     * Creates a single-property observer for the given property.
-     *
-     * @param {string} property Property name
-     * @param {string|function(*,*)} method Function or name of observer method to call
-     * @param {boolean=} dynamicFn Whether the method name should be included as
-     *   a dependency to the effect.
-     * @return {void}
-     * @protected
-     */
-    static createPropertyObserver(property, method, dynamicFn) {
-      this.prototype._createPropertyObserver(property, method, dynamicFn);
-    }
-
-    /**
-     * Creates a multi-property "method observer" based on the provided
-     * expression, which should be a string in the form of a normal JavaScript
-     * function signature: `'methodName(arg1, [..., argn])'`.  Each argument
-     * should correspond to a property or path in the context of this
-     * prototype (or instance), or may be a literal string or number.
-     *
-     * @param {string} expression Method expression
-     * @param {boolean|Object=} dynamicFn Boolean or object map indicating
-     * @return {void}
-     *   whether method names should be included as a dependency to the effect.
-     * @protected
-     */
-    static createMethodObserver(expression, dynamicFn) {
-      this.prototype._createMethodObserver(expression, dynamicFn);
-    }
-
-    /**
-     * Causes the setter for the given property to dispatch `<property>-changed`
-     * events to notify of changes to the property.
-     *
-     * @param {string} property Property name
-     * @return {void}
-     * @protected
-     */
-    static createNotifyingProperty(property) {
-      this.prototype._createNotifyingProperty(property);
-    }
-
-    /**
-     * Creates a read-only accessor for the given property.
-     *
-     * To set the property, use the protected `_setProperty` API.
-     * To create a custom protected setter (e.g. `_setMyProp()` for
-     * property `myProp`), pass `true` for `protectedSetter`.
-     *
-     * Note, if the property will have other property effects, this method
-     * should be called first, before adding other effects.
-     *
-     * @param {string} property Property name
-     * @param {boolean=} protectedSetter Creates a custom protected setter
-     *   when `true`.
-     * @return {void}
-     * @protected
-     */
-    static createReadOnlyProperty(property, protectedSetter) {
-      this.prototype._createReadOnlyProperty(property, protectedSetter);
-    }
-
-    /**
-     * Causes the setter for the given property to reflect the property value
-     * to a (dash-cased) attribute of the same name.
-     *
-     * @param {string} property Property name
-     * @return {void}
-     * @protected
-     */
-    static createReflectedProperty(property) {
-      this.prototype._createReflectedProperty(property);
-    }
-
-    /**
-     * Creates a computed property whose value is set to the result of the
-     * method described by the given `expression` each time one or more
-     * arguments to the method changes.  The expression should be a string
-     * in the form of a normal JavaScript function signature:
-     * `'methodName(arg1, [..., argn])'`
-     *
-     * @param {string} property Name of computed property to set
-     * @param {string} expression Method expression
-     * @param {boolean|Object=} dynamicFn Boolean or object map indicating whether
-     *   method names should be included as a dependency to the effect.
-     * @return {void}
-     * @protected
-     */
-    static createComputedProperty(property, expression, dynamicFn) {
-      this.prototype._createComputedProperty(property, expression, dynamicFn);
-    }
-
-    /**
-     * Parses the provided template to ensure binding effects are created
-     * for them, and then ensures property accessors are created for any
-     * dependent properties in the template.  Binding effects for bound
-     * templates are stored in a linked list on the instance so that
-     * templates can be efficiently stamped and unstamped.
-     *
-     * @param {!HTMLTemplateElement} template Template containing binding
-     *   bindings
-     * @return {!TemplateInfo} Template metadata object
-     * @protected
-     */
-    static bindTemplate(template) {
-      return this.prototype._bindTemplate(template);
-    }
-
-    // -- binding ----------------------------------------------
-
-    /**
-     * Equivalent to static `bindTemplate` API but can be called on
-     * an instance to add effects at runtime.  See that method for
-     * full API docs.
-     *
-     * This method may be called on the prototype (for prototypical template
-     * binding, to avoid creating accessors every instance) once per prototype,
-     * and will be called with `runtimeBinding: true` by `_stampTemplate` to
-     * create and link an instance of the template metadata associated with a
-     * particular stamping.
-     *
-     * @override
-     * @param {!HTMLTemplateElement} template Template containing binding
-     *   bindings
-     * @param {boolean=} instanceBinding When false (default), performs
-     *   "prototypical" binding of the template and overwrites any previously
-     *   bound template for the class. When true (as passed from
-     *   `_stampTemplate`), the template info is instanced and linked into
-     *   the list of bound templates.
-     * @return {!TemplateInfo} Template metadata object; for `runtimeBinding`,
-     *   this is an instance of the prototypical template info
-     * @protected
-     * @suppress {missingProperties} go/missingfnprops
-     */
-    _bindTemplate(template, instanceBinding) {
-      let templateInfo = this.constructor._parseTemplate(template);
-      let wasPreBound = this.__templateInfo == templateInfo;
-      // Optimization: since this is called twice for proto-bound templates,
-      // don't attempt to recreate accessors if this template was pre-bound
-      if (!wasPreBound) {
-        for (let prop in templateInfo.propertyEffects) {
-          this._createPropertyAccessor(prop);
-        }
-      }
-      if (instanceBinding) {
-        // For instance-time binding, create instance of template metadata
-        // and link into list of templates if necessary
-        templateInfo = /** @type {!TemplateInfo} */(Object.create(templateInfo));
-        templateInfo.wasPreBound = wasPreBound;
-        if (!wasPreBound && this.__templateInfo) {
-          let last = this.__templateInfoLast || this.__templateInfo;
-          this.__templateInfoLast = last.nextTemplateInfo = templateInfo;
-          templateInfo.previousTemplateInfo = last;
-          return templateInfo;
-        }
-      }
-      return this.__templateInfo = templateInfo;
-    }
-
-    /**
-     * Adds a property effect to the given template metadata, which is run
-     * at the "propagate" stage of `_propertiesChanged` when the template
-     * has been bound to the element via `_bindTemplate`.
-     *
-     * The `effect` object should match the format in `_addPropertyEffect`.
-     *
-     * @param {Object} templateInfo Template metadata to add effect to
-     * @param {string} prop Property that should trigger the effect
-     * @param {Object=} effect Effect metadata object
-     * @return {void}
-     * @protected
-     */
-    static _addTemplatePropertyEffect(templateInfo, prop, effect) {
-      let hostProps = templateInfo.hostProps = templateInfo.hostProps || {};
-      hostProps[prop] = true;
-      let effects = templateInfo.propertyEffects = templateInfo.propertyEffects || {};
-      let propEffects = effects[prop] = effects[prop] || [];
-      propEffects.push(effect);
-    }
-
-    /**
-     * Stamps the provided template and performs instance-time setup for
-     * Polymer template features, including data bindings, declarative event
-     * listeners, and the `this.$` map of `id`'s to nodes.  A document fragment
-     * is returned containing the stamped DOM, ready for insertion into the
-     * DOM.
-     *
-     * This method may be called more than once; however note that due to
-     * `shadycss` polyfill limitations, only styles from templates prepared
-     * using `ShadyCSS.prepareTemplate` will be correctly polyfilled (scoped
-     * to the shadow root and support CSS custom properties), and note that
-     * `ShadyCSS.prepareTemplate` may only be called once per element. As such,
-     * any styles required by in runtime-stamped templates must be included
-     * in the main element template.
-     *
-     * @param {!HTMLTemplateElement} template Template to stamp
-     * @return {!StampedTemplate} Cloned template content
-     * @override
-     * @protected
-     */
-    _stampTemplate(template) {
-      // Ensures that created dom is `_enqueueClient`'d to this element so
-      // that it can be flushed on next call to `_flushProperties`
-      hostStack.beginHosting(this);
-      let dom = super._stampTemplate(template);
-      hostStack.endHosting(this);
-      let templateInfo = /** @type {!TemplateInfo} */(this._bindTemplate(template, true));
-      // Add template-instance-specific data to instanced templateInfo
-      templateInfo.nodeList = dom.nodeList;
-      // Capture child nodes to allow unstamping of non-prototypical templates
-      if (!templateInfo.wasPreBound) {
-        let nodes = templateInfo.childNodes = [];
-        for (let n=dom.firstChild; n; n=n.nextSibling) {
-          nodes.push(n);
-        }
-      }
-      dom.templateInfo = templateInfo;
-      // Setup compound storage, 2-way listeners, and dataHost for bindings
-      setupBindings(this, templateInfo);
-      // Flush properties into template nodes if already booted
-      if (this.__dataReady) {
-        runEffects(this, templateInfo.propertyEffects, this.__data, null,
-          false, templateInfo.nodeList);
-      }
-      return dom;
-    }
-
-    /**
-     * Removes and unbinds the nodes previously contained in the provided
-     * DocumentFragment returned from `_stampTemplate`.
-     *
-     * @override
-     * @param {!StampedTemplate} dom DocumentFragment previously returned
-     *   from `_stampTemplate` associated with the nodes to be removed
-     * @return {void}
-     * @protected
-     */
-    _removeBoundDom(dom) {
-      // Unlink template info
-      let templateInfo = dom.templateInfo;
-      if (templateInfo.previousTemplateInfo) {
-        templateInfo.previousTemplateInfo.nextTemplateInfo =
-          templateInfo.nextTemplateInfo;
-      }
-      if (templateInfo.nextTemplateInfo) {
-        templateInfo.nextTemplateInfo.previousTemplateInfo =
-          templateInfo.previousTemplateInfo;
-      }
-      if (this.__templateInfoLast == templateInfo) {
-        this.__templateInfoLast = templateInfo.previousTemplateInfo;
-      }
-      templateInfo.previousTemplateInfo = templateInfo.nextTemplateInfo = null;
-      // Remove stamped nodes
-      let nodes = templateInfo.childNodes;
-      for (let i=0; i<nodes.length; i++) {
-        let node = nodes[i];
-        node.parentNode.removeChild(node);
-      }
-    }
-
-    /**
-     * Overrides default `TemplateStamp` implementation to add support for
-     * parsing bindings from `TextNode`'s' `textContent`.  A `bindings`
-     * array is added to `nodeInfo` and populated with binding metadata
-     * with information capturing the binding target, and a `parts` array
-     * with one or more metadata objects capturing the source(s) of the
-     * binding.
-     *
-     * @param {Node} node Node to parse
-     * @param {TemplateInfo} templateInfo Template metadata for current template
-     * @param {NodeInfo} nodeInfo Node metadata for current template node
-     * @return {boolean} `true` if the visited node added node-specific
-     *   metadata to `nodeInfo`
-     * @protected
-     * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
-     */
-    static _parseTemplateNode(node, templateInfo, nodeInfo) {
-      let noted = super._parseTemplateNode(node, templateInfo, nodeInfo);
-      if (node.nodeType === Node.TEXT_NODE) {
-        let parts = this._parseBindings(node.textContent, templateInfo);
-        if (parts) {
-          // Initialize the textContent with any literal parts
-          // NOTE: default to a space here so the textNode remains; some browsers
-          // (IE) omit an empty textNode following cloneNode/importNode.
-          node.textContent = literalFromParts(parts) || ' ';
-          addBinding(this, templateInfo, nodeInfo, 'text', 'textContent', parts);
-          noted = true;
-        }
-      }
-      return noted;
-    }
-
-    /**
-     * Overrides default `TemplateStamp` implementation to add support for
-     * parsing bindings from attributes.  A `bindings`
-     * array is added to `nodeInfo` and populated with binding metadata
-     * with information capturing the binding target, and a `parts` array
-     * with one or more metadata objects capturing the source(s) of the
-     * binding.
-     *
-     * @param {Element} node Node to parse
-     * @param {TemplateInfo} templateInfo Template metadata for current template
-     * @param {NodeInfo} nodeInfo Node metadata for current template node
-     * @param {string} name Attribute name
-     * @param {string} value Attribute value
-     * @return {boolean} `true` if the visited node added node-specific
-     *   metadata to `nodeInfo`
-     * @protected
-     * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
-     */
-    static _parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value) {
-      let parts = this._parseBindings(value, templateInfo);
-      if (parts) {
-        // Attribute or property
-        let origName = name;
-        let kind = 'property';
-        // The only way we see a capital letter here is if the attr has
-        // a capital letter in it per spec. In this case, to make sure
-        // this binding works, we go ahead and make the binding to the attribute.
-        if (capitalAttributeRegex.test(name)) {
-          kind = 'attribute';
-        } else if (name[name.length-1] == '$') {
-          name = name.slice(0, -1);
-          kind = 'attribute';
-        }
-        // Initialize attribute bindings with any literal parts
-        let literal = literalFromParts(parts);
-        if (literal && kind == 'attribute') {
-          // Ensure a ShadyCSS template scoped style is not removed
-          // when a class$ binding's initial literal value is set.
-          if (name == 'class' && node.hasAttribute('class')) {
-            literal += ' ' + node.getAttribute(name);
-          }
-          node.setAttribute(name, literal);
-        }
-        // Clear attribute before removing, since IE won't allow removing
-        // `value` attribute if it previously had a value (can't
-        // unconditionally set '' before removing since attributes with `$`
-        // can't be set using setAttribute)
-        if (node.localName === 'input' && origName === 'value') {
-          node.setAttribute(origName, '');
-        }
-        // Remove annotation
-        node.removeAttribute(origName);
-        // Case hackery: attributes are lower-case, but bind targets
-        // (properties) are case sensitive. Gambit is to map dash-case to
-        // camel-case: `foo-bar` becomes `fooBar`.
-        // Attribute bindings are excepted.
-        if (kind === 'property') {
-          name = dashToCamelCase(name);
-        }
-        addBinding(this, templateInfo, nodeInfo, kind, name, parts, literal);
-        return true;
-      } else {
-        return super._parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value);
-      }
-    }
-
-    /**
-     * Overrides default `TemplateStamp` implementation to add support for
-     * binding the properties that a nested template depends on to the template
-     * as `_host_<property>`.
-     *
-     * @param {Node} node Node to parse
-     * @param {TemplateInfo} templateInfo Template metadata for current template
-     * @param {NodeInfo} nodeInfo Node metadata for current template node
-     * @return {boolean} `true` if the visited node added node-specific
-     *   metadata to `nodeInfo`
-     * @protected
-     * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
-     */
-    static _parseTemplateNestedTemplate(node, templateInfo, nodeInfo) {
-      let noted = super._parseTemplateNestedTemplate(node, templateInfo, nodeInfo);
-      // Merge host props into outer template and add bindings
-      let hostProps = nodeInfo.templateInfo.hostProps;
-      let mode = '{';
-      for (let source in hostProps) {
-        let parts = [{ mode, source, dependencies: [source] }];
-        addBinding(this, templateInfo, nodeInfo, 'property', '_host_' + source, parts);
-      }
-      return noted;
-    }
-
-    /**
-     * Called to parse text in a template (either attribute values or
-     * textContent) into binding metadata.
-     *
-     * Any overrides of this method should return an array of binding part
-     * metadata  representing one or more bindings found in the provided text
-     * and any "literal" text in between.  Any non-literal parts will be passed
-     * to `_evaluateBinding` when any dependencies change.  The only required
-     * fields of each "part" in the returned array are as follows:
-     *
-     * - `dependencies` - Array containing trigger metadata for each property
-     *   that should trigger the binding to update
-     * - `literal` - String containing text if the part represents a literal;
-     *   in this case no `dependencies` are needed
-     *
-     * Additional metadata for use by `_evaluateBinding` may be provided in
-     * each part object as needed.
-     *
-     * The default implementation handles the following types of bindings
-     * (one or more may be intermixed with literal strings):
-     * - Property binding: `[[prop]]`
-     * - Path binding: `[[object.prop]]`
-     * - Negated property or path bindings: `[[!prop]]` or `[[!object.prop]]`
-     * - Two-way property or path bindings (supports negation):
-     *   `{{prop}}`, `{{object.prop}}`, `{{!prop}}` or `{{!object.prop}}`
-     * - Inline computed method (supports negation):
-     *   `[[compute(a, 'literal', b)]]`, `[[!compute(a, 'literal', b)]]`
-     *
-     * The default implementation uses a regular expression for best
-     * performance. However, the regular expression uses a white-list of
-     * allowed characters in a data-binding, which causes problems for
-     * data-bindings that do use characters not in this white-list.
-     *
-     * Instead of updating the white-list with all allowed characters,
-     * there is a StrictBindingParser (see lib/mixins/strict-binding-parser)
-     * that uses a state machine instead. This state machine is able to handle
-     * all characters. However, it is slightly less performant, therefore we
-     * extracted it into a separate optional mixin.
-     *
-     * @param {string} text Text to parse from attribute or textContent
-     * @param {Object} templateInfo Current template metadata
-     * @return {Array<!BindingPart>} Array of binding part metadata
-     * @protected
-     */
-    static _parseBindings(text, templateInfo) {
-      let parts = [];
-      let lastIndex = 0;
-      let m;
-      // Example: "literal1{{prop}}literal2[[!compute(foo,bar)]]final"
-      // Regex matches:
-      //        Iteration 1:  Iteration 2:
-      // m[1]: '{{'          '[['
-      // m[2]: ''            '!'
-      // m[3]: 'prop'        'compute(foo,bar)'
-      while ((m = bindingRegex.exec(text)) !== null) {
-        // Add literal part
-        if (m.index > lastIndex) {
-          parts.push({literal: text.slice(lastIndex, m.index)});
-        }
-        // Add binding part
-        let mode = m[1][0];
-        let negate = Boolean(m[2]);
-        let source = m[3].trim();
-        let customEvent = false, notifyEvent = '', colon = -1;
-        if (mode == '{' && (colon = source.indexOf('::')) > 0) {
-          notifyEvent = source.substring(colon + 2);
-          source = source.substring(0, colon);
-          customEvent = true;
-        }
-        let signature = parseMethod(source);
-        let dependencies = [];
-        if (signature) {
-          // Inline computed function
-          let {args, methodName} = signature;
-          for (let i=0; i<args.length; i++) {
-            let arg = args[i];
-            if (!arg.literal) {
-              dependencies.push(arg);
-            }
-          }
-          let dynamicFns = templateInfo.dynamicFns;
-          if (dynamicFns && dynamicFns[methodName] || signature.static) {
-            dependencies.push(methodName);
-            signature.dynamicFn = true;
-          }
-        } else {
-          // Property or path
-          dependencies.push(source);
-        }
-        parts.push({
-          source, mode, negate, customEvent, signature, dependencies,
-          event: notifyEvent
-        });
-        lastIndex = bindingRegex.lastIndex;
-      }
-      // Add a final literal part
-      if (lastIndex && lastIndex < text.length) {
-        let literal = text.substring(lastIndex);
-        if (literal) {
-          parts.push({
-            literal: literal
-          });
-        }
-      }
-      if (parts.length) {
-        return parts;
-      } else {
-        return null;
-      }
-    }
-
-    /**
-     * Called to evaluate a previously parsed binding part based on a set of
-     * one or more changed dependencies.
-     *
-     * @param {!Polymer_PropertyEffects} inst Element that should be used as
-     *     scope for binding dependencies
-     * @param {BindingPart} part Binding part metadata
-     * @param {string} path Property/path that triggered this effect
-     * @param {Object} props Bag of current property changes
-     * @param {Object} oldProps Bag of previous values for changed properties
-     * @param {boolean} hasPaths True with `props` contains one or more paths
-     * @return {*} Value the binding part evaluated to
-     * @protected
-     */
-    static _evaluateBinding(inst, part, path, props, oldProps, hasPaths) {
-      let value;
-      if (part.signature) {
-        value = runMethodEffect(inst, path, props, oldProps, part.signature);
-      } else if (path != part.source) {
-        value = get(inst, part.source);
-      } else {
-        if (hasPaths && isPath(path)) {
-          value = get(inst, path);
-        } else {
-          value = inst.__data[path];
-        }
-      }
-      if (part.negate) {
-        value = !value;
-      }
-      return value;
-    }
-
-  }
-
-  return PropertyEffects;
-});
-
-/**
- * Helper api for enqueuing client dom created by a host element.
- *
- * By default elements are flushed via `_flushProperties` when
- * `connectedCallback` is called. Elements attach their client dom to
- * themselves at `ready` time which results from this first flush.
- * This provides an ordering guarantee that the client dom an element
- * creates is flushed before the element itself (i.e. client `ready`
- * fires before host `ready`).
- *
- * However, if `_flushProperties` is called *before* an element is connected,
- * as for example `Templatize` does, this ordering guarantee cannot be
- * satisfied because no elements are connected. (Note: Bound elements that
- * receive data do become enqueued clients and are properly ordered but
- * unbound elements are not.)
- *
- * To maintain the desired "client before host" ordering guarantee for this
- * case we rely on the "host stack. Client nodes registers themselves with
- * the creating host element when created. This ensures that all client dom
- * is readied in the proper order, maintaining the desired guarantee.
- *
- * @private
- */
-class HostStack {
-  constructor() {
-    this.stack = [];
-  }
-
-  /**
-   * @param {*} inst Instance to add to hostStack
-   * @return {void}
-   */
-  registerHost(inst) {
-    if (this.stack.length) {
-      let host = this.stack[this.stack.length-1];
-      host._enqueueClient(inst);
-    }
-  }
-
-  /**
-   * @param {*} inst Instance to begin hosting
-   * @return {void}
-   */
-  beginHosting(inst) {
-    this.stack.push(inst);
-  }
-
-  /**
-   * @param {*} inst Instance to end hosting
-   * @return {void}
-   */
-  endHosting(inst) {
-    let stackLen = this.stack.length;
-    if (stackLen && this.stack[stackLen-1] == inst) {
-      this.stack.pop();
-    }
-  }
-}
-const hostStack = new HostStack();
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/strict-binding-parser.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/strict-binding-parser.js
deleted file mode 100644
index 924c69c7..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/strict-binding-parser.js
+++ /dev/null
@@ -1,414 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-
-import { isPath } from '../utils/path.js';
-import { dedupingMixin } from '../utils/mixin.js';
-import { PropertyEffects } from './property-effects.js';
-
-/**
- * The open and corresponding closing brackets for surrounding bindings.
- * @enum {string}
- */
-const BINDINGS = {
-  '{': '}',
-  '[': ']'
-};
-
-/**
- * All states that the parser can be in. The states represent the state-machine as a whole.
- * @enum {number}
- */
-const STATE = {
-  INITIAL: 1,
-  FIRSTOPENINGBINDING: 2,
-  FIRSTCHARACTERBINDING: 3,
-  BINDING: 4,
-  FIRSTCOLON: 5,
-  COLONNOTIFYEVENT: 6,
-  COLONNOTIFYEVENTFIRSTCLOSINGBINDING: 7,
-  FIRSTCLOSINGBINDING: 8,
-  STRING: 9,
-  METHOD: 10,
-  STRINGARG: 11,
-  NUMBERARG: 12,
-  VARIABLEARG: 13,
-  METHODCLOSED: 14,
-  METHODCLOSEDBINDING: 15
-};
-
-function pushLiteral(text, i, parts, startChar) {
-  const literal = text.substring(startChar || 0, i);
-  if (literal) {
-    parts.push({
-      literal
-    });
-  }
-}
-
-function storeMethod(bindingData, templateInfo) {
-  const methodName = bindingData.signature.methodName;
-  const dynamicFns = templateInfo.dynamicFns;
-  if (dynamicFns && dynamicFns[methodName] || bindingData.signature.static) {
-    bindingData.dependencies.push(methodName);
-    bindingData.signature.dynamicFn = true;
-  }
-}
-
-function storeVariableBinding(parts, bindingData, prop, i) {
-  bindingData.source = prop;
-  bindingData.dependencies.push(prop);
-  bindingData.startChar = i + 1;
-  parts.push(bindingData);
-}
-
-function storeMethodVariable(bindingData, text, i) {
-  const name = text.substring(bindingData.startChar, i).trim();
-  if (name) {
-    if (name === 'true' || name === 'false') {
-      bindingData.signature.args.push({
-        name,
-        value: name == 'true',
-        literal: true
-      });
-    } else {
-      const arg = {
-        name
-      };
-      arg.structured = isPath(name);
-      if (arg.structured) {
-        arg.wildcard = (name.slice(-2) == '.*');
-        if (arg.wildcard) {
-          arg.name = name.slice(0, -2);
-        }
-      }
-      bindingData.signature.args.push(arg);
-      bindingData.dependencies.push(name);
-      bindingData.signature.static = false;
-    }
-  }
-}
-
-function storeMethodNumber(bindingData, text, i) {
-  const value = text.substring(bindingData.startChar, i).trim();
-  bindingData.signature.args.push({
-    name: value,
-    value: Number(value),
-    literal: true
-  });
-}
-
-/**
- * Mixin that parses binding expressions and generates corresponding metadata.
- * The implementation is different than in `property-effects`, as it uses a
- * state machine instead of a regex. As such, this implementation is able to
- * handle more cases, with the potential performance hit.
- *
- * @mixinFunction
- * @appliesMixin PropertyEffects
- * @polymer
- * @summary Mixin that parses binding expressions and generates corresponding metadata.
- */
-const StrictBindingParser = dedupingMixin((base) => {
-
-  /**
-   * @constructor
-   * @extends {base}
-   * @implements {Polymer_PropertyEffects}
-   * @private
-   */
-  const elementBase = PropertyEffects(base);
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_PropertyEffects}
-   */
-  return class extends elementBase {
-
-    /**
-     * Called to parse text in a template (either attribute values or
-     * textContent) into binding metadata.
-     *
-     * Any overrides of this method should return an array of binding part
-     * metadata  representing one or more bindings found in the provided text
-     * and any "literal" text in between.  Any non-literal parts will be passed
-     * to `_evaluateBinding` when any dependencies change.  The only required
-     * fields of each "part" in the returned array are as follows:
-     *
-     * - `dependencies` - Array containing trigger metadata for each property
-     *   that should trigger the binding to update
-     * - `literal` - String containing text if the part represents a literal;
-     *   in this case no `dependencies` are needed
-     *
-     * Additional metadata for use by `_evaluateBinding` may be provided in
-     * each part object as needed.
-     *
-     * The default implementation handles the following types of bindings
-     * (one or more may be intermixed with literal strings):
-     * - Property binding: `[[prop]]`
-     * - Path binding: `[[object.prop]]`
-     * - Negated property or path bindings: `[[!prop]]` or `[[!object.prop]]`
-     * - Two-way property or path bindings (supports negation):
-     *   `{{prop}}`, `{{object.prop}}`, `{{!prop}}` or `{{!object.prop}}`
-     * - Inline computed method (supports negation):
-     *   `[[compute(a, 'literal', b)]]`, `[[!compute(a, 'literal', b)]]`
-     *
-     * @param {string} text Text to parse from attribute or textContent
-     * @param {Object} templateInfo Current template metadata
-     * @return {Array<!BindingPart>} Array of binding part metadata
-     * @protected
-     */
-    static _parseBindings(text, templateInfo) {
-      const parts = [];
-      let bindingData = {};
-      let escaped = false;
-      /** @type {string} */
-      let quote;
-      /** @type {number} */
-      let state = STATE.INITIAL;
-      let i,l;
-
-      for (i=0,l=text.length; i<l; i++) {
-        const char = text.charAt(i);
-        switch (state) {
-          case STATE.INITIAL: {
-            if ((char === '{' || char === '[')) {
-              bindingData = {
-                mode: char,
-                dependencies: [],
-                startChar: bindingData.startChar
-              };
-              state = STATE.FIRSTOPENINGBINDING;
-            }
-            break;
-          }
-          case STATE.FIRSTOPENINGBINDING: {
-            if (char === bindingData.mode) {
-              pushLiteral(text, i - 1, parts, bindingData.startChar);
-              bindingData.startChar = i + 1;
-              state = STATE.FIRSTCHARACTERBINDING;
-            } else {
-              bindingData = {};
-              state = STATE.INITIAL;
-            }
-            break;
-          }
-          case STATE.FIRSTCHARACTERBINDING: {
-            if (char !== ' ' && char !== '\t' && char !== '\n') {
-              if (char === '!') {
-                bindingData.negate = true;
-                bindingData.startChar = i + 1;
-              }
-              state = STATE.BINDING;
-            }
-            break;
-          }
-          case STATE.BINDING: {
-            switch (char) {
-              case BINDINGS[bindingData.mode]: {
-                state = STATE.FIRSTCLOSINGBINDING;
-                break;
-              }
-              case '\'':
-              case '"': {
-                quote = char;
-                state = STATE.STRING;
-                break;
-              }
-              case '(': {
-                bindingData.signature = {
-                  methodName: text.substring(bindingData.startChar, i).trim(),
-                  args: [],
-                  static: true
-                };
-                bindingData.startChar = i + 1;
-                state = STATE.METHOD;
-                break;
-              }
-              case ':': {
-                state = STATE.FIRSTCOLON;
-              }
-            }
-            break;
-          }
-          case STATE.FIRSTCOLON: {
-            if (char === ':') {
-              bindingData.customEvent = true;
-              bindingData.startCharAfterColon = i + 1;
-              state = STATE.COLONNOTIFYEVENT;
-            } else {
-              state = STATE.BINDING;
-            }
-            break;
-          }
-          case STATE.COLONNOTIFYEVENT: {
-            if (char === BINDINGS[bindingData.mode]) {
-              state = STATE.COLONNOTIFYEVENTFIRSTCLOSINGBINDING;
-            }
-            break;
-          }
-          case STATE.COLONNOTIFYEVENTFIRSTCLOSINGBINDING: {
-            if (char === BINDINGS[bindingData.mode]) {
-              bindingData.event = text.substring(bindingData.startCharAfterColon, i - 1).trim();
-              const prop = text.substring(bindingData.startChar, bindingData.startCharAfterColon - 2).trim();
-              storeVariableBinding(parts, bindingData, prop, i);
-              state = STATE.INITIAL;
-            } else {
-              state = STATE.BINDING;
-            }
-            break;
-          }
-          case STATE.FIRSTCLOSINGBINDING: {
-            if (char === BINDINGS[bindingData.mode]) {
-              const prop = text.substring(bindingData.startChar, i - 1).trim();
-              storeVariableBinding(parts, bindingData, prop, i);
-              state = STATE.INITIAL;
-            } else {
-              state = STATE.BINDING;
-            }
-            break;
-          }
-          case STATE.STRING: {
-            if (char === '\\') {
-              escaped = true;
-            } else if (char === quote && !escaped) {
-              state = STATE.BINDING;
-            } else {
-              escaped = false;
-            }
-            break;
-          }
-          case STATE.METHOD: {
-            switch (char) {
-              case ')': {
-                storeMethodVariable(bindingData, text, i);
-                storeMethod(bindingData, templateInfo);
-                bindingData.startChar = i + 1;
-                state = STATE.METHODCLOSED;
-                break;
-              }
-              case ',': {
-                storeMethodVariable(bindingData, text, i);
-                bindingData.startChar = i + 1;
-                break;
-              }
-              case '\'':
-              case '"': {
-                quote = char;
-                state = STATE.STRINGARG;
-                break;
-              }
-              default: {
-                if (char >= '0' && char <= '9' || char === '-') {
-                  state = STATE.NUMBERARG;
-                } else if (char != ' ' && char != '\n') {
-                  state = STATE.VARIABLEARG;
-                }
-              }
-            }
-            break;
-          }
-          case STATE.STRINGARG: {
-            if (char === '\\') {
-              escaped = true;
-            } else if (char === quote && !escaped) {
-              const value = text.substring(bindingData.startChar, i)
-                  .replace(/^\s+/, '')
-                  .substring(1)
-                  // replace comma entity with comma
-                  .replace(/&comma;/g, ',')
-                  // repair extra escape sequences; note only commas strictly need
-                  // escaping, but we allow any other char to be escaped since its
-                  // likely users will do this
-                  .replace(/\\(.)/g, '\$1');
-              bindingData.signature.args.push({
-                value,
-                name: value,
-                literal: true
-              });
-              bindingData.startChar = i + 1;
-              state = STATE.METHOD;
-            } else {
-              escaped = false;
-            }
-            break;
-          }
-          case STATE.NUMBERARG: {
-            switch (char) {
-              case ',': {
-                storeMethodNumber(bindingData, text, i);
-                bindingData.startChar = i + 1;
-                state = STATE.METHOD;
-                break;
-              }
-              case ')': {
-                storeMethodNumber(bindingData, text, i);
-                storeMethod(bindingData, templateInfo);
-                state = STATE.METHODCLOSED;
-                break;
-              }
-              default: {
-                if (char < '0' || char > '9') {
-                  state = STATE.VARIABLEARG;
-                }
-              }
-            }
-            break;
-          }
-          case STATE.VARIABLEARG: {
-            switch (char) {
-              case ',': {
-                storeMethodVariable(bindingData, text, i);
-                bindingData.startChar = i + 1;
-                state = STATE.METHOD;
-                break;
-              }
-              case ')': {
-                storeMethodVariable(bindingData, text, i);
-                storeMethod(bindingData, templateInfo);
-                state = STATE.METHODCLOSED;
-                break;
-              }
-            }
-            break;
-          }
-          case STATE.METHODCLOSED: {
-            if (char === BINDINGS[bindingData.mode]) {
-              state = STATE.METHODCLOSEDBINDING;
-            } else if (char !== ' ' && char !== '\t' && char !== '\n') {
-              console.warn(`Expected two closing "${BINDINGS[bindingData.mode]}" for binding "${text}"`);
-            }
-            break;
-          }
-          case STATE.METHODCLOSEDBINDING: {
-            if (char === BINDINGS[bindingData.mode]) {
-              bindingData.startChar = i + 1;
-              parts.push(bindingData);
-              state = STATE.INITIAL;
-            } else if (char !== ' ' && char !== '\t' && char !== '\n') {
-              console.warn(`Expected one closing "${BINDINGS[bindingData.mode]}" for binding "${text}"`);
-            }
-            break;
-          }
-        }
-      }
-
-      if (parts.length) {
-        pushLiteral(text, i, parts, parts[parts.length - 1].startChar);
-        return parts;
-      }
-
-      return null;
-    }
-  };
-});
-
-export { StrictBindingParser };
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/template-stamp.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/template-stamp.js
deleted file mode 100644
index 167fbe53..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/template-stamp.js
+++ /dev/null
@@ -1,500 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import '../utils/boot.js';
-
-import { dedupingMixin } from '../utils/mixin.js';
-
-const walker = document.createTreeWalker(document, NodeFilter.SHOW_ALL,
-    null, false);
-
-// 1.x backwards-compatible auto-wrapper for template type extensions
-// This is a clear layering violation and gives favored-nation status to
-// dom-if and dom-repeat templates.  This is a conceit we're choosing to keep
-// a.) to ease 1.x backwards-compatibility due to loss of `is`, and
-// b.) to maintain if/repeat capability in parser-constrained elements
-//     (e.g. table, select) in lieu of native CE type extensions without
-//     massive new invention in this space (e.g. directive system)
-const templateExtensions = {
-  'dom-if': true,
-  'dom-repeat': true
-};
-function wrapTemplateExtension(node) {
-  let is = node.getAttribute('is');
-  if (is && templateExtensions[is]) {
-    let t = node;
-    t.removeAttribute('is');
-    node = t.ownerDocument.createElement(is);
-    t.parentNode.replaceChild(node, t);
-    node.appendChild(t);
-    while(t.attributes.length) {
-      node.setAttribute(t.attributes[0].name, t.attributes[0].value);
-      t.removeAttribute(t.attributes[0].name);
-    }
-  }
-  return node;
-}
-
-function findTemplateNode(root, nodeInfo) {
-  // recursively ascend tree until we hit root
-  let parent = nodeInfo.parentInfo && findTemplateNode(root, nodeInfo.parentInfo);
-  // unwind the stack, returning the indexed node at each level
-  if (parent) {
-    // note: marginally faster than indexing via childNodes
-    // (http://jsperf.com/childnodes-lookup)
-    walker.currentNode = parent;
-    for (let n=walker.firstChild(), i=0; n; n=walker.nextSibling()) {
-      if (nodeInfo.parentIndex === i++) {
-        return n;
-      }
-    }
-  } else {
-    return root;
-  }
-}
-
-// construct `$` map (from id annotations)
-function applyIdToMap(inst, map, node, nodeInfo) {
-  if (nodeInfo.id) {
-    map[nodeInfo.id] = node;
-  }
-}
-
-// install event listeners (from event annotations)
-function applyEventListener(inst, node, nodeInfo) {
-  if (nodeInfo.events && nodeInfo.events.length) {
-    for (let j=0, e$=nodeInfo.events, e; (j<e$.length) && (e=e$[j]); j++) {
-      inst._addMethodEventListenerToNode(node, e.name, e.value, inst);
-    }
-  }
-}
-
-// push configuration references at configure time
-function applyTemplateContent(inst, node, nodeInfo) {
-  if (nodeInfo.templateInfo) {
-    node._templateInfo = nodeInfo.templateInfo;
-  }
-}
-
-function createNodeEventHandler(context, eventName, methodName) {
-  // Instances can optionally have a _methodHost which allows redirecting where
-  // to find methods. Currently used by `templatize`.
-  context = context._methodHost || context;
-  let handler = function(e) {
-    if (context[methodName]) {
-      context[methodName](e, e.detail);
-    } else {
-      console.warn('listener method `' + methodName + '` not defined');
-    }
-  };
-  return handler;
-}
-
-/**
- * Element mixin that provides basic template parsing and stamping, including
- * the following template-related features for stamped templates:
- *
- * - Declarative event listeners (`on-eventname="listener"`)
- * - Map of node id's to stamped node instances (`this.$.id`)
- * - Nested template content caching/removal and re-installation (performance
- *   optimization)
- *
- * @mixinFunction
- * @polymer
- * @summary Element class mixin that provides basic template parsing and stamping
- */
-export const TemplateStamp = dedupingMixin(
-    /**
-     * @template T
-     * @param {function(new:T)} superClass Class to apply mixin to.
-     * @return {function(new:T)} superClass with mixin applied.
-     */
-    (superClass) => {
-
-  /**
-   * @polymer
-   * @mixinClass
-   * @implements {Polymer_TemplateStamp}
-   */
-  class TemplateStamp extends superClass {
-
-    /**
-     * Scans a template to produce template metadata.
-     *
-     * Template-specific metadata are stored in the object returned, and node-
-     * specific metadata are stored in objects in its flattened `nodeInfoList`
-     * array.  Only nodes in the template that were parsed as nodes of
-     * interest contain an object in `nodeInfoList`.  Each `nodeInfo` object
-     * contains an `index` (`childNodes` index in parent) and optionally
-     * `parent`, which points to node info of its parent (including its index).
-     *
-     * The template metadata object returned from this method has the following
-     * structure (many fields optional):
-     *
-     * ```js
-     *   {
-     *     // Flattened list of node metadata (for nodes that generated metadata)
-     *     nodeInfoList: [
-     *       {
-     *         // `id` attribute for any nodes with id's for generating `$` map
-     *         id: {string},
-     *         // `on-event="handler"` metadata
-     *         events: [
-     *           {
-     *             name: {string},   // event name
-     *             value: {string},  // handler method name
-     *           }, ...
-     *         ],
-     *         // Notes when the template contained a `<slot>` for shady DOM
-     *         // optimization purposes
-     *         hasInsertionPoint: {boolean},
-     *         // For nested `<template>`` nodes, nested template metadata
-     *         templateInfo: {object}, // nested template metadata
-     *         // Metadata to allow efficient retrieval of instanced node
-     *         // corresponding to this metadata
-     *         parentInfo: {number},   // reference to parent nodeInfo>
-     *         parentIndex: {number},  // index in parent's `childNodes` collection
-     *         infoIndex: {number},    // index of this `nodeInfo` in `templateInfo.nodeInfoList`
-     *       },
-     *       ...
-     *     ],
-     *     // When true, the template had the `strip-whitespace` attribute
-     *     // or was nested in a template with that setting
-     *     stripWhitespace: {boolean},
-     *     // For nested templates, nested template content is moved into
-     *     // a document fragment stored here; this is an optimization to
-     *     // avoid the cost of nested template cloning
-     *     content: {DocumentFragment}
-     *   }
-     * ```
-     *
-     * This method kicks off a recursive treewalk as follows:
-     *
-     * ```
-     *    _parseTemplate <---------------------+
-     *      _parseTemplateContent              |
-     *        _parseTemplateNode  <------------|--+
-     *          _parseTemplateNestedTemplate --+  |
-     *          _parseTemplateChildNodes ---------+
-     *          _parseTemplateNodeAttributes
-     *            _parseTemplateNodeAttribute
-     *
-     * ```
-     *
-     * These methods may be overridden to add custom metadata about templates
-     * to either `templateInfo` or `nodeInfo`.
-     *
-     * Note that this method may be destructive to the template, in that
-     * e.g. event annotations may be removed after being noted in the
-     * template metadata.
-     *
-     * @param {!HTMLTemplateElement} template Template to parse
-     * @param {TemplateInfo=} outerTemplateInfo Template metadata from the outer
-     *   template, for parsing nested templates
-     * @return {!TemplateInfo} Parsed template metadata
-     */
-    static _parseTemplate(template, outerTemplateInfo) {
-      // since a template may be re-used, memo-ize metadata
-      if (!template._templateInfo) {
-        let templateInfo = template._templateInfo = {};
-        templateInfo.nodeInfoList = [];
-        templateInfo.stripWhiteSpace =
-          (outerTemplateInfo && outerTemplateInfo.stripWhiteSpace) ||
-          template.hasAttribute('strip-whitespace');
-        this._parseTemplateContent(template, templateInfo, {parent: null});
-      }
-      return template._templateInfo;
-    }
-
-    static _parseTemplateContent(template, templateInfo, nodeInfo) {
-      return this._parseTemplateNode(template.content, templateInfo, nodeInfo);
-    }
-
-    /**
-     * Parses template node and adds template and node metadata based on
-     * the current node, and its `childNodes` and `attributes`.
-     *
-     * This method may be overridden to add custom node or template specific
-     * metadata based on this node.
-     *
-     * @param {Node} node Node to parse
-     * @param {!TemplateInfo} templateInfo Template metadata for current template
-     * @param {!NodeInfo} nodeInfo Node metadata for current template.
-     * @return {boolean} `true` if the visited node added node-specific
-     *   metadata to `nodeInfo`
-     */
-    static _parseTemplateNode(node, templateInfo, nodeInfo) {
-      let noted;
-      let element = /** @type {Element} */(node);
-      if (element.localName == 'template' && !element.hasAttribute('preserve-content')) {
-        noted = this._parseTemplateNestedTemplate(element, templateInfo, nodeInfo) || noted;
-      } else if (element.localName === 'slot') {
-        // For ShadyDom optimization, indicating there is an insertion point
-        templateInfo.hasInsertionPoint = true;
-      }
-      walker.currentNode = element;
-      if (walker.firstChild()) {
-        noted = this._parseTemplateChildNodes(element, templateInfo, nodeInfo) || noted;
-      }
-      if (element.hasAttributes && element.hasAttributes()) {
-        noted = this._parseTemplateNodeAttributes(element, templateInfo, nodeInfo) || noted;
-      }
-      return noted;
-    }
-
-    /**
-     * Parses template child nodes for the given root node.
-     *
-     * This method also wraps whitelisted legacy template extensions
-     * (`is="dom-if"` and `is="dom-repeat"`) with their equivalent element
-     * wrappers, collapses text nodes, and strips whitespace from the template
-     * if the `templateInfo.stripWhitespace` setting was provided.
-     *
-     * @param {Node} root Root node whose `childNodes` will be parsed
-     * @param {!TemplateInfo} templateInfo Template metadata for current template
-     * @param {!NodeInfo} nodeInfo Node metadata for current template.
-     * @return {void}
-     */
-    static _parseTemplateChildNodes(root, templateInfo, nodeInfo) {
-      if (root.localName === 'script' || root.localName === 'style') {
-        return;
-      }
-      walker.currentNode = root;
-      for (let node=walker.firstChild(), parentIndex=0, next; node; node=next) {
-        // Wrap templates
-        if (node.localName == 'template') {
-          node = wrapTemplateExtension(node);
-        }
-        // collapse adjacent textNodes: fixes an IE issue that can cause
-        // text nodes to be inexplicably split =(
-        // note that root.normalize() should work but does not so we do this
-        // manually.
-        walker.currentNode = node;
-        next = walker.nextSibling();
-        if (node.nodeType === Node.TEXT_NODE) {
-          let /** Node */ n = next;
-          while (n && (n.nodeType === Node.TEXT_NODE)) {
-            node.textContent += n.textContent;
-            next = walker.nextSibling();
-            root.removeChild(n);
-            n = next;
-          }
-          // optionally strip whitespace
-          if (templateInfo.stripWhiteSpace && !node.textContent.trim()) {
-            root.removeChild(node);
-            continue;
-          }
-        }
-        let childInfo = { parentIndex, parentInfo: nodeInfo };
-        if (this._parseTemplateNode(node, templateInfo, childInfo)) {
-          childInfo.infoIndex = templateInfo.nodeInfoList.push(/** @type {!NodeInfo} */(childInfo)) - 1;
-        }
-        // Increment if not removed
-        walker.currentNode = node;
-        if (walker.parentNode()) {
-          parentIndex++;
-        }
-      }
-    }
-
-    /**
-     * Parses template content for the given nested `<template>`.
-     *
-     * Nested template info is stored as `templateInfo` in the current node's
-     * `nodeInfo`. `template.content` is removed and stored in `templateInfo`.
-     * It will then be the responsibility of the host to set it back to the
-     * template and for users stamping nested templates to use the
-     * `_contentForTemplate` method to retrieve the content for this template
-     * (an optimization to avoid the cost of cloning nested template content).
-     *
-     * @param {HTMLTemplateElement} node Node to parse (a <template>)
-     * @param {TemplateInfo} outerTemplateInfo Template metadata for current template
-     *   that includes the template `node`
-     * @param {!NodeInfo} nodeInfo Node metadata for current template.
-     * @return {boolean} `true` if the visited node added node-specific
-     *   metadata to `nodeInfo`
-     */
-    static _parseTemplateNestedTemplate(node, outerTemplateInfo, nodeInfo) {
-      let templateInfo = this._parseTemplate(node, outerTemplateInfo);
-      let content = templateInfo.content =
-        node.content.ownerDocument.createDocumentFragment();
-      content.appendChild(node.content);
-      nodeInfo.templateInfo = templateInfo;
-      return true;
-    }
-
-    /**
-     * Parses template node attributes and adds node metadata to `nodeInfo`
-     * for nodes of interest.
-     *
-     * @param {Element} node Node to parse
-     * @param {TemplateInfo} templateInfo Template metadata for current template
-     * @param {NodeInfo} nodeInfo Node metadata for current template.
-     * @return {boolean} `true` if the visited node added node-specific
-     *   metadata to `nodeInfo`
-     */
-    static _parseTemplateNodeAttributes(node, templateInfo, nodeInfo) {
-      // Make copy of original attribute list, since the order may change
-      // as attributes are added and removed
-      let noted = false;
-      let attrs = Array.from(node.attributes);
-      for (let i=attrs.length-1, a; (a=attrs[i]); i--) {
-        noted = this._parseTemplateNodeAttribute(node, templateInfo, nodeInfo, a.name, a.value) || noted;
-      }
-      return noted;
-    }
-
-    /**
-     * Parses a single template node attribute and adds node metadata to
-     * `nodeInfo` for attributes of interest.
-     *
-     * This implementation adds metadata for `on-event="handler"` attributes
-     * and `id` attributes.
-     *
-     * @param {Element} node Node to parse
-     * @param {!TemplateInfo} templateInfo Template metadata for current template
-     * @param {!NodeInfo} nodeInfo Node metadata for current template.
-     * @param {string} name Attribute name
-     * @param {string} value Attribute value
-     * @return {boolean} `true` if the visited node added node-specific
-     *   metadata to `nodeInfo`
-     */
-    static _parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value) {
-      // events (on-*)
-      if (name.slice(0, 3) === 'on-') {
-        node.removeAttribute(name);
-        nodeInfo.events = nodeInfo.events || [];
-        nodeInfo.events.push({
-          name: name.slice(3),
-          value
-        });
-        return true;
-      }
-      // static id
-      else if (name === 'id') {
-        nodeInfo.id = value;
-        return true;
-      }
-      return false;
-    }
-
-    /**
-     * Returns the `content` document fragment for a given template.
-     *
-     * For nested templates, Polymer performs an optimization to cache nested
-     * template content to avoid the cost of cloning deeply nested templates.
-     * This method retrieves the cached content for a given template.
-     *
-     * @param {HTMLTemplateElement} template Template to retrieve `content` for
-     * @return {DocumentFragment} Content fragment
-     */
-    static _contentForTemplate(template) {
-      let templateInfo = /** @type {HTMLTemplateElementWithInfo} */ (template)._templateInfo;
-      return (templateInfo && templateInfo.content) || template.content;
-    }
-
-    /**
-     * Clones the provided template content and returns a document fragment
-     * containing the cloned dom.
-     *
-     * The template is parsed (once and memoized) using this library's
-     * template parsing features, and provides the following value-added
-     * features:
-     * * Adds declarative event listeners for `on-event="handler"` attributes
-     * * Generates an "id map" for all nodes with id's under `$` on returned
-     *   document fragment
-     * * Passes template info including `content` back to templates as
-     *   `_templateInfo` (a performance optimization to avoid deep template
-     *   cloning)
-     *
-     * Note that the memoized template parsing process is destructive to the
-     * template: attributes for bindings and declarative event listeners are
-     * removed after being noted in notes, and any nested `<template>.content`
-     * is removed and stored in notes as well.
-     *
-     * @param {!HTMLTemplateElement} template Template to stamp
-     * @return {!StampedTemplate} Cloned template content
-     * @override
-     */
-    _stampTemplate(template) {
-      // Polyfill support: bootstrap the template if it has not already been
-      if (template && !template.content &&
-          window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
-        HTMLTemplateElement.decorate(template);
-      }
-      let templateInfo = this.constructor._parseTemplate(template);
-      let nodeInfo = templateInfo.nodeInfoList;
-      let content = templateInfo.content || template.content;
-      let dom = /** @type {DocumentFragment} */ (document.importNode(content, true));
-      // NOTE: ShadyDom optimization indicating there is an insertion point
-      dom.__noInsertionPoint = !templateInfo.hasInsertionPoint;
-      let nodes = dom.nodeList = new Array(nodeInfo.length);
-      dom.$ = {};
-      for (let i=0, l=nodeInfo.length, info; (i<l) && (info=nodeInfo[i]); i++) {
-        let node = nodes[i] = findTemplateNode(dom, info);
-        applyIdToMap(this, dom.$, node, info);
-        applyTemplateContent(this, node, info);
-        applyEventListener(this, node, info);
-      }
-      dom = /** @type {!StampedTemplate} */(dom); // eslint-disable-line no-self-assign
-      return dom;
-    }
-
-    /**
-     * Adds an event listener by method name for the event provided.
-     *
-     * This method generates a handler function that looks up the method
-     * name at handling time.
-     *
-     * @param {!EventTarget} node Node to add listener on
-     * @param {string} eventName Name of event
-     * @param {string} methodName Name of method
-     * @param {*=} context Context the method will be called on (defaults
-     *   to `node`)
-     * @return {Function} Generated handler function
-     * @override
-     */
-    _addMethodEventListenerToNode(node, eventName, methodName, context) {
-      context = context || node;
-      let handler = createNodeEventHandler(context, eventName, methodName);
-      this._addEventListenerToNode(node, eventName, handler);
-      return handler;
-    }
-
-    /**
-     * Override point for adding custom or simulated event handling.
-     *
-     * @param {!EventTarget} node Node to add event listener to
-     * @param {string} eventName Name of event
-     * @param {function(!Event):void} handler Listener function to add
-     * @return {void}
-     * @override
-     */
-    _addEventListenerToNode(node, eventName, handler) {
-      node.addEventListener(eventName, handler);
-    }
-
-    /**
-     * Override point for adding custom or simulated event handling.
-     *
-     * @param {!EventTarget} node Node to remove event listener from
-     * @param {string} eventName Name of event
-     * @param {function(!Event):void} handler Listener function to remove
-     * @return {void}
-     * @override
-     */
-    _removeEventListenerFromNode(node, eventName, handler) {
-      node.removeEventListener(eventName, handler);
-    }
-
-  }
-
-  return TemplateStamp;
-
-});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/array-splice.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/array-splice.js
deleted file mode 100644
index 77ccb1a..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/array-splice.js
+++ /dev/null
@@ -1,299 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-function newSplice(index, removed, addedCount) {
-  return {
-    index: index,
-    removed: removed,
-    addedCount: addedCount
-  };
-}
-
-const EDIT_LEAVE = 0;
-const EDIT_UPDATE = 1;
-const EDIT_ADD = 2;
-const EDIT_DELETE = 3;
-
-// Note: This function is *based* on the computation of the Levenshtein
-// "edit" distance. The one change is that "updates" are treated as two
-// edits - not one. With Array splices, an update is really a delete
-// followed by an add. By retaining this, we optimize for "keeping" the
-// maximum array items in the original array. For example:
-//
-//   'xxxx123' -> '123yyyy'
-//
-// With 1-edit updates, the shortest path would be just to update all seven
-// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
-// leaves the substring '123' intact.
-function calcEditDistances(current, currentStart, currentEnd,
-                            old, oldStart, oldEnd) {
-  // "Deletion" columns
-  let rowCount = oldEnd - oldStart + 1;
-  let columnCount = currentEnd - currentStart + 1;
-  let distances = new Array(rowCount);
-
-  // "Addition" rows. Initialize null column.
-  for (let i = 0; i < rowCount; i++) {
-    distances[i] = new Array(columnCount);
-    distances[i][0] = i;
-  }
-
-  // Initialize null row
-  for (let j = 0; j < columnCount; j++)
-    distances[0][j] = j;
-
-  for (let i = 1; i < rowCount; i++) {
-    for (let j = 1; j < columnCount; j++) {
-      if (equals(current[currentStart + j - 1], old[oldStart + i - 1]))
-        distances[i][j] = distances[i - 1][j - 1];
-      else {
-        let north = distances[i - 1][j] + 1;
-        let west = distances[i][j - 1] + 1;
-        distances[i][j] = north < west ? north : west;
-      }
-    }
-  }
-
-  return distances;
-}
-
-// This starts at the final weight, and walks "backward" by finding
-// the minimum previous weight recursively until the origin of the weight
-// matrix.
-function spliceOperationsFromEditDistances(distances) {
-  let i = distances.length - 1;
-  let j = distances[0].length - 1;
-  let current = distances[i][j];
-  let edits = [];
-  while (i > 0 || j > 0) {
-    if (i == 0) {
-      edits.push(EDIT_ADD);
-      j--;
-      continue;
-    }
-    if (j == 0) {
-      edits.push(EDIT_DELETE);
-      i--;
-      continue;
-    }
-    let northWest = distances[i - 1][j - 1];
-    let west = distances[i - 1][j];
-    let north = distances[i][j - 1];
-
-    let min;
-    if (west < north)
-      min = west < northWest ? west : northWest;
-    else
-      min = north < northWest ? north : northWest;
-
-    if (min == northWest) {
-      if (northWest == current) {
-        edits.push(EDIT_LEAVE);
-      } else {
-        edits.push(EDIT_UPDATE);
-        current = northWest;
-      }
-      i--;
-      j--;
-    } else if (min == west) {
-      edits.push(EDIT_DELETE);
-      i--;
-      current = west;
-    } else {
-      edits.push(EDIT_ADD);
-      j--;
-      current = north;
-    }
-  }
-
-  edits.reverse();
-  return edits;
-}
-
-/**
- * Splice Projection functions:
- *
- * A splice map is a representation of how a previous array of items
- * was transformed into a new array of items. Conceptually it is a list of
- * tuples of
- *
- *   <index, removed, addedCount>
- *
- * which are kept in ascending index order of. The tuple represents that at
- * the |index|, |removed| sequence of items were removed, and counting forward
- * from |index|, |addedCount| items were added.
- */
-
-/**
- * Lacking individual splice mutation information, the minimal set of
- * splices can be synthesized given the previous state and final state of an
- * array. The basic approach is to calculate the edit distance matrix and
- * choose the shortest path through it.
- *
- * Complexity: O(l * p)
- *   l: The length of the current array
- *   p: The length of the old array
- *
- * @param {!Array} current The current "changed" array for which to
- * calculate splices.
- * @param {number} currentStart Starting index in the `current` array for
- * which splices are calculated.
- * @param {number} currentEnd Ending index in the `current` array for
- * which splices are calculated.
- * @param {!Array} old The original "unchanged" array to compare `current`
- * against to determine splices.
- * @param {number} oldStart Starting index in the `old` array for
- * which splices are calculated.
- * @param {number} oldEnd Ending index in the `old` array for
- * which splices are calculated.
- * @return {!Array} Returns an array of splice record objects. Each of these
- * contains: `index` the location where the splice occurred; `removed`
- * the array of removed items from this location; `addedCount` the number
- * of items added at this location.
- */
-function calcSplices(current, currentStart, currentEnd,
-                      old, oldStart, oldEnd) {
-  let prefixCount = 0;
-  let suffixCount = 0;
-  let splice;
-
-  let minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
-  if (currentStart == 0 && oldStart == 0)
-    prefixCount = sharedPrefix(current, old, minLength);
-
-  if (currentEnd == current.length && oldEnd == old.length)
-    suffixCount = sharedSuffix(current, old, minLength - prefixCount);
-
-  currentStart += prefixCount;
-  oldStart += prefixCount;
-  currentEnd -= suffixCount;
-  oldEnd -= suffixCount;
-
-  if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
-    return [];
-
-  if (currentStart == currentEnd) {
-    splice = newSplice(currentStart, [], 0);
-    while (oldStart < oldEnd)
-      splice.removed.push(old[oldStart++]);
-
-    return [ splice ];
-  } else if (oldStart == oldEnd)
-    return [ newSplice(currentStart, [], currentEnd - currentStart) ];
-
-  let ops = spliceOperationsFromEditDistances(
-      calcEditDistances(current, currentStart, currentEnd,
-                             old, oldStart, oldEnd));
-
-  splice = undefined;
-  let splices = [];
-  let index = currentStart;
-  let oldIndex = oldStart;
-  for (let i = 0; i < ops.length; i++) {
-    switch(ops[i]) {
-      case EDIT_LEAVE:
-        if (splice) {
-          splices.push(splice);
-          splice = undefined;
-        }
-
-        index++;
-        oldIndex++;
-        break;
-      case EDIT_UPDATE:
-        if (!splice)
-          splice = newSplice(index, [], 0);
-
-        splice.addedCount++;
-        index++;
-
-        splice.removed.push(old[oldIndex]);
-        oldIndex++;
-        break;
-      case EDIT_ADD:
-        if (!splice)
-          splice = newSplice(index, [], 0);
-
-        splice.addedCount++;
-        index++;
-        break;
-      case EDIT_DELETE:
-        if (!splice)
-          splice = newSplice(index, [], 0);
-
-        splice.removed.push(old[oldIndex]);
-        oldIndex++;
-        break;
-    }
-  }
-
-  if (splice) {
-    splices.push(splice);
-  }
-  return splices;
-}
-
-function sharedPrefix(current, old, searchLength) {
-  for (let i = 0; i < searchLength; i++)
-    if (!equals(current[i], old[i]))
-      return i;
-  return searchLength;
-}
-
-function sharedSuffix(current, old, searchLength) {
-  let index1 = current.length;
-  let index2 = old.length;
-  let count = 0;
-  while (count < searchLength && equals(current[--index1], old[--index2]))
-    count++;
-
-  return count;
-}
-
-/**
- * Returns an array of splice records indicating the minimum edits required
- * to transform the `previous` array into the `current` array.
- *
- * Splice records are ordered by index and contain the following fields:
- * - `index`: index where edit started
- * - `removed`: array of removed items from this index
- * - `addedCount`: number of items added at this index
- *
- * This function is based on the Levenshtein "minimum edit distance"
- * algorithm. Note that updates are treated as removal followed by addition.
- *
- * The worst-case time complexity of this algorithm is `O(l * p)`
- *   l: The length of the current array
- *   p: The length of the previous array
- *
- * However, the worst-case complexity is reduced by an `O(n)` optimization
- * to detect any shared prefix & suffix between the two arrays and only
- * perform the more expensive minimum edit distance calculation over the
- * non-shared portions of the arrays.
- *
- * @function
- * @param {!Array} current The "changed" array for which splices will be
- * calculated.
- * @param {!Array} previous The "unchanged" original array to compare
- * `current` against to determine the splices.
- * @return {!Array} Returns an array of splice record objects. Each of these
- * contains: `index` the location where the splice occurred; `removed`
- * the array of removed items from this location; `addedCount` the number
- * of items added at this location.
- */
-export function calculateSplices(current, previous) {
-  return calcSplices(current, 0, current.length, previous, 0,
-                          previous.length);
-}
-
-function equals(currentValue, previousValue) {
-  return currentValue === previousValue;
-}
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/async.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/async.js
deleted file mode 100644
index e332f95..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/async.js
+++ /dev/null
@@ -1,207 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/**
- * @fileoverview
- *
- * This module provides a number of strategies for enqueuing asynchronous
- * tasks. Each sub-module provides a standard `run(fn)` interface that returns a
- * handle, and a `cancel(handle)` interface for canceling async tasks before
- * they run.
- *
- * @summary Module that provides a number of strategies for enqueuing
- * asynchronous tasks.
- */
-
-import './boot.js';
-
-// Microtask implemented using Mutation Observer
-let microtaskCurrHandle = 0;
-let microtaskLastHandle = 0;
-let microtaskCallbacks = [];
-let microtaskNodeContent = 0;
-let microtaskNode = document.createTextNode('');
-new window.MutationObserver(microtaskFlush).observe(microtaskNode, {characterData: true});
-
-function microtaskFlush() {
-  const len = microtaskCallbacks.length;
-  for (let i = 0; i < len; i++) {
-    let cb = microtaskCallbacks[i];
-    if (cb) {
-      try {
-        cb();
-      } catch (e) {
-        setTimeout(() => { throw e; });
-      }
-    }
-  }
-  microtaskCallbacks.splice(0, len);
-  microtaskLastHandle += len;
-}
-
-/**
- * Async interface wrapper around `setTimeout`.
- *
- * @namespace
- * @summary Async interface wrapper around `setTimeout`.
- */
-const timeOut = {
-  /**
-   * Returns a sub-module with the async interface providing the provided
-   * delay.
-   *
-   * @memberof timeOut
-   * @param {number=} delay Time to wait before calling callbacks in ms
-   * @return {!AsyncInterface} An async timeout interface
-   */
-  after(delay) {
-    return {
-      run(fn) { return window.setTimeout(fn, delay); },
-      cancel(handle) {
-        window.clearTimeout(handle);
-      }
-    };
-  },
-  /**
-   * Enqueues a function called in the next task.
-   *
-   * @memberof timeOut
-   * @param {!Function} fn Callback to run
-   * @param {number=} delay Delay in milliseconds
-   * @return {number} Handle used for canceling task
-   */
-  run(fn, delay) {
-    return window.setTimeout(fn, delay);
-  },
-  /**
-   * Cancels a previously enqueued `timeOut` callback.
-   *
-   * @memberof timeOut
-   * @param {number} handle Handle returned from `run` of callback to cancel
-   * @return {void}
-   */
-  cancel(handle) {
-    window.clearTimeout(handle);
-  }
-};
-export {timeOut};
-
-/**
- * Async interface wrapper around `requestAnimationFrame`.
- *
- * @namespace
- * @summary Async interface wrapper around `requestAnimationFrame`.
- */
-const animationFrame = {
-  /**
-   * Enqueues a function called at `requestAnimationFrame` timing.
-   *
-   * @memberof animationFrame
-   * @param {function(number):void} fn Callback to run
-   * @return {number} Handle used for canceling task
-   */
-  run(fn) {
-    return window.requestAnimationFrame(fn);
-  },
-  /**
-   * Cancels a previously enqueued `animationFrame` callback.
-   *
-   * @memberof animationFrame
-   * @param {number} handle Handle returned from `run` of callback to cancel
-   * @return {void}
-   */
-  cancel(handle) {
-    window.cancelAnimationFrame(handle);
-  }
-};
-export {animationFrame};
-
-/**
- * Async interface wrapper around `requestIdleCallback`.  Falls back to
- * `setTimeout` on browsers that do not support `requestIdleCallback`.
- *
- * @namespace
- * @summary Async interface wrapper around `requestIdleCallback`.
- */
-const idlePeriod = {
-  /**
-   * Enqueues a function called at `requestIdleCallback` timing.
-   *
-   * @memberof idlePeriod
-   * @param {function(!IdleDeadline):void} fn Callback to run
-   * @return {number} Handle used for canceling task
-   */
-  run(fn) {
-    return window.requestIdleCallback ?
-      window.requestIdleCallback(fn) :
-      window.setTimeout(fn, 16);
-  },
-  /**
-   * Cancels a previously enqueued `idlePeriod` callback.
-   *
-   * @memberof idlePeriod
-   * @param {number} handle Handle returned from `run` of callback to cancel
-   * @return {void}
-   */
-  cancel(handle) {
-    window.cancelIdleCallback ?
-      window.cancelIdleCallback(handle) :
-      window.clearTimeout(handle);
-  }
-};
-export {idlePeriod};
-
-/**
- * Async interface for enqueuing callbacks that run at microtask timing.
- *
- * Note that microtask timing is achieved via a single `MutationObserver`,
- * and thus callbacks enqueued with this API will all run in a single
- * batch, and not interleaved with other microtasks such as promises.
- * Promises are avoided as an implementation choice for the time being
- * due to Safari bugs that cause Promises to lack microtask guarantees.
- *
- * @namespace
- * @summary Async interface for enqueuing callbacks that run at microtask
- *   timing.
- */
-const microTask = {
-
-  /**
-   * Enqueues a function called at microtask timing.
-   *
-   * @memberof microTask
-   * @param {!Function=} callback Callback to run
-   * @return {number} Handle used for canceling task
-   */
-  run(callback) {
-    microtaskNode.textContent = microtaskNodeContent++;
-    microtaskCallbacks.push(callback);
-    return microtaskCurrHandle++;
-  },
-
-  /**
-   * Cancels a previously enqueued `microTask` callback.
-   *
-   * @memberof microTask
-   * @param {number} handle Handle returned from `run` of callback to cancel
-   * @return {void}
-   */
-  cancel(handle) {
-    const idx = handle - microtaskLastHandle;
-    if (idx >= 0) {
-      if (!microtaskCallbacks[idx]) {
-        throw new Error('invalid async handle: ' + handle);
-      }
-      microtaskCallbacks[idx] = null;
-    }
-  }
-
-};
-export {microTask};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/boot.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/boot.js
deleted file mode 100644
index d2bce39..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/boot.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/* eslint-disable no-unused-vars */
-/**
- * When using Closure Compiler, JSCompiler_renameProperty(property, object) is replaced by the munged name for object[property]
- * We cannot alias this function, so we have to use a small shim that has the same behavior when not compiling.
- *
- * @param {string} prop Property name
- * @param {?Object} obj Reference object
- * @return {string} Potentially renamed property name
- */
-window.JSCompiler_renameProperty = function(prop, obj) {
-  return prop;
-};
-/* eslint-enable */
-
-export {};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/case-map.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/case-map.js
deleted file mode 100644
index 1963b02..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/case-map.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-const caseMap = {};
-const DASH_TO_CAMEL = /-[a-z]/g;
-const CAMEL_TO_DASH = /([A-Z])/g;
-
-/**
- * @fileoverview Module with utilities for converting between "dash-case" and
- * "camelCase" identifiers.
- */
-
-/**
- * Converts "dash-case" identifier (e.g. `foo-bar-baz`) to "camelCase"
- * (e.g. `fooBarBaz`).
- *
- * @param {string} dash Dash-case identifier
- * @return {string} Camel-case representation of the identifier
- */
-export function dashToCamelCase(dash) {
-  return caseMap[dash] || (
-    caseMap[dash] = dash.indexOf('-') < 0 ? dash : dash.replace(DASH_TO_CAMEL,
-      (m) => m[1].toUpperCase()
-    )
-  );
-}
-
-/**
- * Converts "camelCase" identifier (e.g. `fooBarBaz`) to "dash-case"
- * (e.g. `foo-bar-baz`).
- *
- * @param {string} camel Camel-case identifier
- * @return {string} Dash-case representation of the identifier
- */
-export function camelToDashCase(camel) {
-  return caseMap[camel] || (
-    caseMap[camel] = camel.replace(CAMEL_TO_DASH, '-$1').toLowerCase()
-  );
-}
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/debounce.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/debounce.js
deleted file mode 100644
index cf4311abbc..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/debounce.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-import './mixin.js';
-import './async.js';
-
-/**
- * @summary Collapse multiple callbacks into one invocation after a timer.
- */
-export class Debouncer {
-  constructor() {
-    this._asyncModule = null;
-    this._callback = null;
-    this._timer = null;
-  }
-  /**
-   * Sets the scheduler; that is, a module with the Async interface,
-   * a callback and optional arguments to be passed to the run function
-   * from the async module.
-   *
-   * @param {!AsyncInterface} asyncModule Object with Async interface.
-   * @param {function()} callback Callback to run.
-   * @return {void}
-   */
-  setConfig(asyncModule, callback) {
-    this._asyncModule = asyncModule;
-    this._callback = callback;
-    this._timer = this._asyncModule.run(() => {
-      this._timer = null;
-      debouncerQueue.delete(this);
-      this._callback();
-    });
-  }
-  /**
-   * Cancels an active debouncer and returns a reference to itself.
-   *
-   * @return {void}
-   */
-  cancel() {
-    if (this.isActive()) {
-      this._cancelAsync();
-      // Canceling a debouncer removes its spot from the flush queue,
-      // so if a debouncer is manually canceled and re-debounced, it
-      // will reset its flush order (this is a very minor difference from 1.x)
-      // Re-debouncing via the `debounce` API retains the 1.x FIFO flush order
-      debouncerQueue.delete(this);
-    }
-  }
-  /**
-   * Cancels a debouncer's async callback.
-   *
-   * @return {void}
-   */
-  _cancelAsync() {
-    if (this.isActive()) {
-      this._asyncModule.cancel(/** @type {number} */(this._timer));
-      this._timer = null;
-    }
-  }
-  /**
-   * Flushes an active debouncer and returns a reference to itself.
-   *
-   * @return {void}
-   */
-  flush() {
-    if (this.isActive()) {
-      this.cancel();
-      this._callback();
-    }
-  }
-  /**
-   * Returns true if the debouncer is active.
-   *
-   * @return {boolean} True if active.
-   */
-  isActive() {
-    return this._timer != null;
-  }
-  /**
-   * Creates a debouncer if no debouncer is passed as a parameter
-   * or it cancels an active debouncer otherwise. The following
-   * example shows how a debouncer can be called multiple times within a
-   * microtask and "debounced" such that the provided callback function is
-   * called once. Add this method to a custom element:
-   *
-   * ```js
-   * import {microTask} from '@polymer/polymer/lib/utils/async.js';
-   * import {Debouncer} from '@polymer/polymer/lib/utils/debounce.js';
-   * // ...
-   *
-   * _debounceWork() {
-   *   this._debounceJob = Debouncer.debounce(this._debounceJob,
-   *       microTask, () => this._doWork());
-   * }
-   * ```
-   *
-   * If the `_debounceWork` method is called multiple times within the same
-   * microtask, the `_doWork` function will be called only once at the next
-   * microtask checkpoint.
-   *
-   * Note: In testing it is often convenient to avoid asynchrony. To accomplish
-   * this with a debouncer, you can use `enqueueDebouncer` and
-   * `flush`. For example, extend the above example by adding
-   * `enqueueDebouncer(this._debounceJob)` at the end of the
-   * `_debounceWork` method. Then in a test, call `flush` to ensure
-   * the debouncer has completed.
-   *
-   * @param {Debouncer?} debouncer Debouncer object.
-   * @param {!AsyncInterface} asyncModule Object with Async interface
-   * @param {function()} callback Callback to run.
-   * @return {!Debouncer} Returns a debouncer object.
-   */
-  static debounce(debouncer, asyncModule, callback) {
-    if (debouncer instanceof Debouncer) {
-      // Cancel the async callback, but leave in debouncerQueue if it was
-      // enqueued, to maintain 1.x flush order
-      debouncer._cancelAsync();
-    } else {
-      debouncer = new Debouncer();
-    }
-    debouncer.setConfig(asyncModule, callback);
-    return debouncer;
-  }
-}
-
-let debouncerQueue = new Set();
-
-/**
- * Adds a `Debouncer` to a list of globally flushable tasks.
- *
- * @param {!Debouncer} debouncer Debouncer to enqueue
- * @return {void}
- */
-export const enqueueDebouncer = function(debouncer) {
-  debouncerQueue.add(debouncer);
-};
-
-/**
- * Flushes any enqueued debouncers
- *
- * @return {boolean} Returns whether any debouncers were flushed
- */
-export const flushDebouncers = function() {
-  const didFlush = Boolean(debouncerQueue.size);
-  // If new debouncers are added while flushing, Set.forEach will ensure
-  // newly added ones are also flushed
-  debouncerQueue.forEach(debouncer => {
-    try {
-      debouncer.flush();
-    } catch(e) {
-      setTimeout(() => {
-        throw e;
-      });
-    }
-  });
-  return didFlush;
-};
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flattened-nodes-observer.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flattened-nodes-observer.js
deleted file mode 100644
index ddc8b74..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flattened-nodes-observer.js
+++ /dev/null
@@ -1,314 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-import { calculateSplices } from './array-splice.js';
-import { microTask } from './async.js';
-import { wrap } from './wrap.js';
-
-/**
- * Returns true if `node` is a slot element
- * @param {!Node} node Node to test.
- * @return {boolean} Returns true if the given `node` is a slot
- * @private
- */
-function isSlot(node) {
-  return (node.localName === 'slot');
-}
-
-/**
- * Class that listens for changes (additions or removals) to
- * "flattened nodes" on a given `node`. The list of flattened nodes consists
- * of a node's children and, for any children that are `<slot>` elements,
- * the expanded flattened list of `assignedNodes`.
- * For example, if the observed node has children `<a></a><slot></slot><b></b>`
- * and the `<slot>` has one `<div>` assigned to it, then the flattened
- * nodes list is `<a></a><div></div><b></b>`. If the `<slot>` has other
- * `<slot>` elements assigned to it, these are flattened as well.
- *
- * The provided `callback` is called whenever any change to this list
- * of flattened nodes occurs, where an addition or removal of a node is
- * considered a change. The `callback` is called with one argument, an object
- * containing an array of any `addedNodes` and `removedNodes`.
- *
- * Note: the callback is called asynchronous to any changes
- * at a microtask checkpoint. This is because observation is performed using
- * `MutationObserver` and the `<slot>` element's `slotchange` event which
- * are asynchronous.
- *
- * An example:
- * ```js
- * class TestSelfObserve extends PolymerElement {
- *   static get is() { return 'test-self-observe';}
- *   connectedCallback() {
- *     super.connectedCallback();
- *     this._observer = new FlattenedNodesObserver(this, (info) => {
- *       this.info = info;
- *     });
- *   }
- *   disconnectedCallback() {
- *     super.disconnectedCallback();
- *     this._observer.disconnect();
- *   }
- * }
- * customElements.define(TestSelfObserve.is, TestSelfObserve);
- * ```
- *
- * @summary Class that listens for changes (additions or removals) to
- * "flattened nodes" on a given `node`.
- * @implements {PolymerDomApi.ObserveHandle}
- */
-export let FlattenedNodesObserver = class {
-
-  /**
-   * Returns the list of flattened nodes for the given `node`.
-   * This list consists of a node's children and, for any children
-   * that are `<slot>` elements, the expanded flattened list of `assignedNodes`.
-   * For example, if the observed node has children `<a></a><slot></slot><b></b>`
-   * and the `<slot>` has one `<div>` assigned to it, then the flattened
-   * nodes list is `<a></a><div></div><b></b>`. If the `<slot>` has other
-   * `<slot>` elements assigned to it, these are flattened as well.
-   *
-   * @param {!HTMLElement|!HTMLSlotElement} node The node for which to
-   *      return the list of flattened nodes.
-   * @return {!Array<!Node>} The list of flattened nodes for the given `node`.
-   * @nocollapse See https://github.com/google/closure-compiler/issues/2763
-   */
-  // eslint-disable-next-line
-  static getFlattenedNodes(node) {
-    const wrapped = wrap(node);
-    if (isSlot(node)) {
-      node = /** @type {!HTMLSlotElement} */(node); // eslint-disable-line no-self-assign
-      return wrapped.assignedNodes({flatten: true});
-    } else {
-      return Array.from(wrapped.childNodes).map((node) => {
-        if (isSlot(node)) {
-          node = /** @type {!HTMLSlotElement} */(node); // eslint-disable-line no-self-assign
-          return wrap(node).assignedNodes({flatten: true});
-        } else {
-          return [node];
-        }
-      }).reduce((a, b) => a.concat(b), []);
-    }
-  }
-
-  /**
-   * @param {!HTMLElement} target Node on which to listen for changes.
-   * @param {?function(this: Element, { target: !HTMLElement, addedNodes: !Array<!Element>, removedNodes: !Array<!Element> }):void} callback Function called when there are additions
-   * or removals from the target's list of flattened nodes.
-   */
-  // eslint-disable-next-line
-  constructor(target, callback) {
-    /**
-     * @type {MutationObserver}
-     * @private
-     */
-    this._shadyChildrenObserver = null;
-    /**
-     * @type {MutationObserver}
-     * @private
-     */
-    this._nativeChildrenObserver = null;
-    this._connected = false;
-    /**
-     * @type {!HTMLElement}
-     * @private
-     */
-    this._target = target;
-    this.callback = callback;
-    this._effectiveNodes = [];
-    this._observer = null;
-    this._scheduled = false;
-    /**
-     * @type {function()}
-     * @private
-     */
-    this._boundSchedule = () => {
-      this._schedule();
-    };
-    this.connect();
-    this._schedule();
-  }
-
-  /**
-   * Activates an observer. This method is automatically called when
-   * a `FlattenedNodesObserver` is created. It should only be called to
-   * re-activate an observer that has been deactivated via the `disconnect` method.
-   *
-   * @return {void}
-   */
-  connect() {
-    if (isSlot(this._target)) {
-      this._listenSlots([this._target]);
-    } else if (wrap(this._target).children) {
-      this._listenSlots(
-          /** @type {!NodeList<!Node>} */ (wrap(this._target).children));
-      if (window.ShadyDOM) {
-        this._shadyChildrenObserver =
-          ShadyDOM.observeChildren(this._target, (mutations) => {
-            this._processMutations(mutations);
-          });
-      } else {
-        this._nativeChildrenObserver =
-          new MutationObserver((mutations) => {
-            this._processMutations(mutations);
-          });
-        this._nativeChildrenObserver.observe(this._target, {childList: true});
-      }
-    }
-    this._connected = true;
-  }
-
-  /**
-   * Deactivates the flattened nodes observer. After calling this method
-   * the observer callback will not be called when changes to flattened nodes
-   * occur. The `connect` method may be subsequently called to reactivate
-   * the observer.
-   *
-   * @return {void}
-   * @override
-   */
-  disconnect() {
-    if (isSlot(this._target)) {
-      this._unlistenSlots([this._target]);
-    } else if (wrap(this._target).children) {
-      this._unlistenSlots(
-          /** @type {!NodeList<!Node>} */ (wrap(this._target).children));
-      if (window.ShadyDOM && this._shadyChildrenObserver) {
-        ShadyDOM.unobserveChildren(this._shadyChildrenObserver);
-        this._shadyChildrenObserver = null;
-      } else if (this._nativeChildrenObserver) {
-        this._nativeChildrenObserver.disconnect();
-        this._nativeChildrenObserver = null;
-      }
-    }
-    this._connected = false;
-  }
-
-  /**
-   * @return {void}
-   * @private
-   */
-  _schedule() {
-    if (!this._scheduled) {
-      this._scheduled = true;
-      microTask.run(() => this.flush());
-    }
-  }
-
-  /**
-   * @param {Array<MutationRecord>} mutations Mutations signaled by the mutation observer
-   * @return {void}
-   * @private
-   */
-  _processMutations(mutations) {
-    this._processSlotMutations(mutations);
-    this.flush();
-  }
-
-  /**
-   * @param {Array<MutationRecord>} mutations Mutations signaled by the mutation observer
-   * @return {void}
-   * @private
-   */
-  _processSlotMutations(mutations) {
-    if (mutations) {
-      for (let i=0; i < mutations.length; i++) {
-        let mutation = mutations[i];
-        if (mutation.addedNodes) {
-          this._listenSlots(mutation.addedNodes);
-        }
-        if (mutation.removedNodes) {
-          this._unlistenSlots(mutation.removedNodes);
-        }
-      }
-    }
-  }
-
-  /**
-   * Flushes the observer causing any pending changes to be immediately
-   * delivered the observer callback. By default these changes are delivered
-   * asynchronously at the next microtask checkpoint.
-   *
-   * @return {boolean} Returns true if any pending changes caused the observer
-   * callback to run.
-   */
-  flush() {
-    if (!this._connected) {
-      return false;
-    }
-    if (window.ShadyDOM) {
-      ShadyDOM.flush();
-    }
-    if (this._nativeChildrenObserver) {
-      this._processSlotMutations(this._nativeChildrenObserver.takeRecords());
-    } else if (this._shadyChildrenObserver) {
-      this._processSlotMutations(this._shadyChildrenObserver.takeRecords());
-    }
-    this._scheduled = false;
-    let info = {
-      target: this._target,
-      addedNodes: [],
-      removedNodes: []
-    };
-    let newNodes = this.constructor.getFlattenedNodes(this._target);
-    let splices = calculateSplices(newNodes,
-      this._effectiveNodes);
-    // process removals
-    for (let i=0, s; (i<splices.length) && (s=splices[i]); i++) {
-      for (let j=0, n; (j < s.removed.length) && (n=s.removed[j]); j++) {
-        info.removedNodes.push(n);
-      }
-    }
-    // process adds
-    for (let i=0, s; (i<splices.length) && (s=splices[i]); i++) {
-      for (let j=s.index; j < s.index + s.addedCount; j++) {
-        info.addedNodes.push(newNodes[j]);
-      }
-    }
-    // update cache
-    this._effectiveNodes = newNodes;
-    let didFlush = false;
-    if (info.addedNodes.length || info.removedNodes.length) {
-      didFlush = true;
-      this.callback.call(this._target, info);
-    }
-    return didFlush;
-  }
-
-  /**
-   * @param {!Array<!Node>|!NodeList<!Node>} nodeList Nodes that could change
-   * @return {void}
-   * @private
-   */
-  _listenSlots(nodeList) {
-    for (let i=0; i < nodeList.length; i++) {
-      let n = nodeList[i];
-      if (isSlot(n)) {
-        n.addEventListener('slotchange', this._boundSchedule);
-      }
-    }
-  }
-
-  /**
-   * @param {!Array<!Node>|!NodeList<!Node>} nodeList Nodes that could change
-   * @return {void}
-   * @private
-   */
-  _unlistenSlots(nodeList) {
-    for (let i=0; i < nodeList.length; i++) {
-      let n = nodeList[i];
-      if (isSlot(n)) {
-        n.removeEventListener('slotchange', this._boundSchedule);
-      }
-    }
-  }
-
-};
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flush.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flush.js
deleted file mode 100644
index a7190470..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flush.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-/* eslint-disable no-unused-vars */
-import { Debouncer } from '../utils/debounce.js';  // used in type annotations
-/* eslint-enable no-unused-vars */
-import { flushDebouncers } from '../utils/debounce.js';  // used in type annotations
-export { enqueueDebouncer } from '../utils/debounce.js';  // used in type annotations
-
-/**
- * Forces several classes of asynchronously queued tasks to flush:
- * - Debouncers added via `enqueueDebouncer`
- * - ShadyDOM distribution
- *
- * @return {void}
- */
-export const flush = function() {
-  let shadyDOM, debouncers;
-  do {
-    shadyDOM = window.ShadyDOM && ShadyDOM.flush();
-    if (window.ShadyCSS && window.ShadyCSS.ScopingShim) {
-      window.ShadyCSS.ScopingShim.flush();
-    }
-    debouncers = flushDebouncers();
-  } while (shadyDOM || debouncers);
-};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/gestures.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/gestures.js
deleted file mode 100644
index f5f4bfd..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/gestures.js
+++ /dev/null
@@ -1,1079 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/**
- * @fileoverview
- *
- * Module for adding listeners to a node for the following normalized
- * cross-platform "gesture" events:
- * - `down` - mouse or touch went down
- * - `up` - mouse or touch went up
- * - `tap` - mouse click or finger tap
- * - `track` - mouse drag or touch move
- *
- * @summary Module for adding cross-platform gesture event listeners.
- */
-
-import './boot.js';
-
-import { timeOut, microTask } from './async.js';
-import { Debouncer } from './debounce.js';
-import { passiveTouchGestures } from './settings.js';
-import { wrap } from './wrap.js';
-
-// detect native touch action support
-let HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string';
-let GESTURE_KEY = '__polymerGestures';
-let HANDLED_OBJ = '__polymerGesturesHandled';
-let TOUCH_ACTION = '__polymerGesturesTouchAction';
-// radius for tap and track
-let TAP_DISTANCE = 25;
-let TRACK_DISTANCE = 5;
-// number of last N track positions to keep
-let TRACK_LENGTH = 2;
-
-// Disabling "mouse" handlers for 2500ms is enough
-let MOUSE_TIMEOUT = 2500;
-let MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'click'];
-// an array of bitmask values for mapping MouseEvent.which to MouseEvent.buttons
-let MOUSE_WHICH_TO_BUTTONS = [0, 1, 4, 2];
-let MOUSE_HAS_BUTTONS = (function() {
-  try {
-    return new MouseEvent('test', {buttons: 1}).buttons === 1;
-  } catch (e) {
-    return false;
-  }
-})();
-
-/**
- * @param {string} name Possible mouse event name
- * @return {boolean} true if mouse event, false if not
- */
-function isMouseEvent(name) {
-  return MOUSE_EVENTS.indexOf(name) > -1;
-}
-
-/* eslint no-empty: ["error", { "allowEmptyCatch": true }] */
-// check for passive event listeners
-let SUPPORTS_PASSIVE = false;
-(function() {
-  try {
-    let opts = Object.defineProperty({}, 'passive', {get() {SUPPORTS_PASSIVE = true;}});
-    window.addEventListener('test', null, opts);
-    window.removeEventListener('test', null, opts);
-  } catch(e) {}
-})();
-
-/**
- * Generate settings for event listeners, dependant on `passiveTouchGestures`
- *
- * @param {string} eventName Event name to determine if `{passive}` option is
- *   needed
- * @return {{passive: boolean} | undefined} Options to use for addEventListener
- *   and removeEventListener
- */
-function PASSIVE_TOUCH(eventName) {
-  if (isMouseEvent(eventName) || eventName === 'touchend') {
-    return;
-  }
-  if (HAS_NATIVE_TA && SUPPORTS_PASSIVE && passiveTouchGestures) {
-    return {passive: true};
-  } else {
-    return;
-  }
-}
-
-// Check for touch-only devices
-let IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);
-
-// keep track of any labels hit by the mouseCanceller
-/** @type {!Array<!HTMLLabelElement>} */
-const clickedLabels = [];
-
-/** @type {!Object<boolean>} */
-const labellable = {
-  'button': true,
-  'input': true,
-  'keygen': true,
-  'meter': true,
-  'output': true,
-  'textarea': true,
-  'progress': true,
-  'select': true
-};
-
-// Defined at https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute
-/** @type {!Object<boolean>} */
-const canBeDisabled = {
-  'button': true,
-  'command': true,
-  'fieldset': true,
-  'input': true,
-  'keygen': true,
-  'optgroup': true,
-  'option': true,
-  'select': true,
-  'textarea': true
-};
-
-/**
- * @param {HTMLElement} el Element to check labelling status
- * @return {boolean} element can have labels
- */
-function canBeLabelled(el) {
-  return labellable[el.localName] || false;
-}
-
-/**
- * @param {HTMLElement} el Element that may be labelled.
- * @return {!Array<!HTMLLabelElement>} Relevant label for `el`
- */
-function matchingLabels(el) {
-  let labels = Array.prototype.slice.call(/** @type {HTMLInputElement} */(el).labels || []);
-  // IE doesn't have `labels` and Safari doesn't populate `labels`
-  // if element is in a shadowroot.
-  // In this instance, finding the non-ancestor labels is enough,
-  // as the mouseCancellor code will handle ancstor labels
-  if (!labels.length) {
-    labels = [];
-    let root = el.getRootNode();
-    // if there is an id on `el`, check for all labels with a matching `for` attribute
-    if (el.id) {
-      let matching = root.querySelectorAll(`label[for = ${el.id}]`);
-      for (let i = 0; i < matching.length; i++) {
-        labels.push(/** @type {!HTMLLabelElement} */(matching[i]));
-      }
-    }
-  }
-  return labels;
-}
-
-// touch will make synthetic mouse events
-// `preventDefault` on touchend will cancel them,
-// but this breaks `<input>` focus and link clicks
-// disable mouse handlers for MOUSE_TIMEOUT ms after
-// a touchend to ignore synthetic mouse events
-let mouseCanceller = function(mouseEvent) {
-  // Check for sourceCapabilities, used to distinguish synthetic events
-  // if mouseEvent did not come from a device that fires touch events,
-  // it was made by a real mouse and should be counted
-  // http://wicg.github.io/InputDeviceCapabilities/#dom-inputdevicecapabilities-firestouchevents
-  let sc = mouseEvent.sourceCapabilities;
-  if (sc && !sc.firesTouchEvents) {
-    return;
-  }
-  // skip synthetic mouse events
-  mouseEvent[HANDLED_OBJ] = {skip: true};
-  // disable "ghost clicks"
-  if (mouseEvent.type === 'click') {
-    let clickFromLabel = false;
-    let path = getComposedPath(mouseEvent);
-    for (let i = 0; i < path.length; i++) {
-      if (path[i].nodeType === Node.ELEMENT_NODE) {
-        if (path[i].localName === 'label') {
-          clickedLabels.push(/** @type {!HTMLLabelElement} */ (path[i]));
-        } else if (canBeLabelled(/** @type {!HTMLElement} */ (path[i]))) {
-          let ownerLabels =
-              matchingLabels(/** @type {!HTMLElement} */ (path[i]));
-          // check if one of the clicked labels is labelling this element
-          for (let j = 0; j < ownerLabels.length; j++) {
-            clickFromLabel = clickFromLabel || clickedLabels.indexOf(ownerLabels[j]) > -1;
-          }
-        }
-      }
-      if (path[i] === POINTERSTATE.mouse.target) {
-        return;
-      }
-    }
-    // if one of the clicked labels was labelling the target element,
-    // this is not a ghost click
-    if (clickFromLabel) {
-      return;
-    }
-    mouseEvent.preventDefault();
-    mouseEvent.stopPropagation();
-  }
-};
-
-/**
- * @param {boolean=} setup True to add, false to remove.
- * @return {void}
- */
-function setupTeardownMouseCanceller(setup) {
-  let events = IS_TOUCH_ONLY ? ['click'] : MOUSE_EVENTS;
-  for (let i = 0, en; i < events.length; i++) {
-    en = events[i];
-    if (setup) {
-      // reset clickLabels array
-      clickedLabels.length = 0;
-      document.addEventListener(en, mouseCanceller, true);
-    } else {
-      document.removeEventListener(en, mouseCanceller, true);
-    }
-  }
-}
-
-function ignoreMouse(e) {
-  if (!POINTERSTATE.mouse.mouseIgnoreJob) {
-    setupTeardownMouseCanceller(true);
-  }
-  let unset = function() {
-    setupTeardownMouseCanceller();
-    POINTERSTATE.mouse.target = null;
-    POINTERSTATE.mouse.mouseIgnoreJob = null;
-  };
-  POINTERSTATE.mouse.target = getComposedPath(e)[0];
-  POINTERSTATE.mouse.mouseIgnoreJob = Debouncer.debounce(
-        POINTERSTATE.mouse.mouseIgnoreJob
-      , timeOut.after(MOUSE_TIMEOUT)
-      , unset);
-}
-
-/**
- * @param {MouseEvent} ev event to test for left mouse button down
- * @return {boolean} has left mouse button down
- */
-function hasLeftMouseButton(ev) {
-  let type = ev.type;
-  // exit early if the event is not a mouse event
-  if (!isMouseEvent(type)) {
-    return false;
-  }
-  // ev.button is not reliable for mousemove (0 is overloaded as both left button and no buttons)
-  // instead we use ev.buttons (bitmask of buttons) or fall back to ev.which (deprecated, 0 for no buttons, 1 for left button)
-  if (type === 'mousemove') {
-    // allow undefined for testing events
-    let buttons = ev.buttons === undefined ? 1 : ev.buttons;
-    if ((ev instanceof window.MouseEvent) && !MOUSE_HAS_BUTTONS) {
-      buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0;
-    }
-    // buttons is a bitmask, check that the left button bit is set (1)
-    return Boolean(buttons & 1);
-  } else {
-    // allow undefined for testing events
-    let button = ev.button === undefined ? 0 : ev.button;
-    // ev.button is 0 in mousedown/mouseup/click for left button activation
-    return button === 0;
-  }
-}
-
-function isSyntheticClick(ev) {
-  if (ev.type === 'click') {
-    // ev.detail is 0 for HTMLElement.click in most browsers
-    if (ev.detail === 0) {
-      return true;
-    }
-    // in the worst case, check that the x/y position of the click is within
-    // the bounding box of the target of the event
-    // Thanks IE 10 >:(
-    let t = _findOriginalTarget(ev);
-    // make sure the target of the event is an element so we can use getBoundingClientRect,
-    // if not, just assume it is a synthetic click
-    if (!t.nodeType || /** @type {Element} */(t).nodeType !== Node.ELEMENT_NODE) {
-      return true;
-    }
-    let bcr = /** @type {Element} */(t).getBoundingClientRect();
-    // use page x/y to account for scrolling
-    let x = ev.pageX, y = ev.pageY;
-    // ev is a synthetic click if the position is outside the bounding box of the target
-    return !((x >= bcr.left && x <= bcr.right) && (y >= bcr.top && y <= bcr.bottom));
-  }
-  return false;
-}
-
-let POINTERSTATE = {
-  mouse: {
-    target: null,
-    mouseIgnoreJob: null
-  },
-  touch: {
-    x: 0,
-    y: 0,
-    id: -1,
-    scrollDecided: false
-  }
-};
-
-function firstTouchAction(ev) {
-  let ta = 'auto';
-  let path = getComposedPath(ev);
-  for (let i = 0, n; i < path.length; i++) {
-    n = path[i];
-    if (n[TOUCH_ACTION]) {
-      ta = n[TOUCH_ACTION];
-      break;
-    }
-  }
-  return ta;
-}
-
-function trackDocument(stateObj, movefn, upfn) {
-  stateObj.movefn = movefn;
-  stateObj.upfn = upfn;
-  document.addEventListener('mousemove', movefn);
-  document.addEventListener('mouseup', upfn);
-}
-
-function untrackDocument(stateObj) {
-  document.removeEventListener('mousemove', stateObj.movefn);
-  document.removeEventListener('mouseup', stateObj.upfn);
-  stateObj.movefn = null;
-  stateObj.upfn = null;
-}
-
-// use a document-wide touchend listener to start the ghost-click prevention mechanism
-// Use passive event listeners, if supported, to not affect scrolling performance
-document.addEventListener('touchend', ignoreMouse, SUPPORTS_PASSIVE ? {passive: true} : false);
-
-/**
- * Returns the composedPath for the given event.
- * @param {Event} event to process
- * @return {!Array<!EventTarget>} Path of the event
- */
-const getComposedPath = window.ShadyDOM && window.ShadyDOM.noPatch ?
-  window.ShadyDOM.composedPath :
-  (event) => event.composedPath && event.composedPath() || [];
-
-/** @type {!Object<string, !GestureRecognizer>} */
-export const gestures = {};
-
-/** @type {!Array<!GestureRecognizer>} */
-export const recognizers = [];
-
-/**
- * Finds the element rendered on the screen at the provided coordinates.
- *
- * Similar to `document.elementFromPoint`, but pierces through
- * shadow roots.
- *
- * @param {number} x Horizontal pixel coordinate
- * @param {number} y Vertical pixel coordinate
- * @return {Element} Returns the deepest shadowRoot inclusive element
- * found at the screen position given.
- */
-export function deepTargetFind(x, y) {
-  let node = document.elementFromPoint(x, y);
-  let next = node;
-  // this code path is only taken when native ShadowDOM is used
-  // if there is a shadowroot, it may have a node at x/y
-  // if there is not a shadowroot, exit the loop
-  while (next && next.shadowRoot && !window.ShadyDOM) {
-    // if there is a node at x/y in the shadowroot, look deeper
-    let oldNext = next;
-    next = next.shadowRoot.elementFromPoint(x, y);
-    // on Safari, elementFromPoint may return the shadowRoot host
-    if (oldNext === next) {
-      break;
-    }
-    if (next) {
-      node = next;
-    }
-  }
-  return node;
-}
-
-/**
- * a cheaper check than ev.composedPath()[0];
- *
- * @private
- * @param {Event|Touch} ev Event.
- * @return {EventTarget} Returns the event target.
- */
-function _findOriginalTarget(ev) {
-  const path = getComposedPath(/** @type {?Event} */ (ev));
-  // It shouldn't be, but sometimes path is empty (window on Safari).
-  return path.length > 0 ? path[0] : ev.target;
-}
-
-/**
- * @private
- * @param {Event} ev Event.
- * @return {void}
- */
-function _handleNative(ev) {
-  let handled;
-  let type = ev.type;
-  let node = ev.currentTarget;
-  let gobj = node[GESTURE_KEY];
-  if (!gobj) {
-    return;
-  }
-  let gs = gobj[type];
-  if (!gs) {
-    return;
-  }
-  if (!ev[HANDLED_OBJ]) {
-    ev[HANDLED_OBJ] = {};
-    if (type.slice(0, 5) === 'touch') {
-      ev = /** @type {TouchEvent} */(ev); // eslint-disable-line no-self-assign
-      let t = ev.changedTouches[0];
-      if (type === 'touchstart') {
-        // only handle the first finger
-        if (ev.touches.length === 1) {
-          POINTERSTATE.touch.id = t.identifier;
-        }
-      }
-      if (POINTERSTATE.touch.id !== t.identifier) {
-        return;
-      }
-      if (!HAS_NATIVE_TA) {
-        if (type === 'touchstart' || type === 'touchmove') {
-          _handleTouchAction(ev);
-        }
-      }
-    }
-  }
-  handled = ev[HANDLED_OBJ];
-  // used to ignore synthetic mouse events
-  if (handled.skip) {
-    return;
-  }
-  // reset recognizer state
-  for (let i = 0, r; i < recognizers.length; i++) {
-    r = recognizers[i];
-    if (gs[r.name] && !handled[r.name]) {
-      if (r.flow && r.flow.start.indexOf(ev.type) > -1 && r.reset) {
-        r.reset();
-      }
-    }
-  }
-  // enforce gesture recognizer order
-  for (let i = 0, r; i < recognizers.length; i++) {
-    r = recognizers[i];
-    if (gs[r.name] && !handled[r.name]) {
-      handled[r.name] = true;
-      r[type](ev);
-    }
-  }
-}
-
-/**
- * @private
- * @param {TouchEvent} ev Event.
- * @return {void}
- */
-function _handleTouchAction(ev) {
-  let t = ev.changedTouches[0];
-  let type = ev.type;
-  if (type === 'touchstart') {
-    POINTERSTATE.touch.x = t.clientX;
-    POINTERSTATE.touch.y = t.clientY;
-    POINTERSTATE.touch.scrollDecided = false;
-  } else if (type === 'touchmove') {
-    if (POINTERSTATE.touch.scrollDecided) {
-      return;
-    }
-    POINTERSTATE.touch.scrollDecided = true;
-    let ta = firstTouchAction(ev);
-    let shouldPrevent = false;
-    let dx = Math.abs(POINTERSTATE.touch.x - t.clientX);
-    let dy = Math.abs(POINTERSTATE.touch.y - t.clientY);
-    if (!ev.cancelable) {
-      // scrolling is happening
-    } else if (ta === 'none') {
-      shouldPrevent = true;
-    } else if (ta === 'pan-x') {
-      shouldPrevent = dy > dx;
-    } else if (ta === 'pan-y') {
-      shouldPrevent = dx > dy;
-    }
-    if (shouldPrevent) {
-      ev.preventDefault();
-    } else {
-      prevent('track');
-    }
-  }
-}
-
-/**
- * Adds an event listener to a node for the given gesture type.
- *
- * @param {!EventTarget} node Node to add listener on
- * @param {string} evType Gesture type: `down`, `up`, `track`, or `tap`
- * @param {!function(!Event):void} handler Event listener function to call
- * @return {boolean} Returns true if a gesture event listener was added.
- */
-export function addListener(node, evType, handler) {
-  if (gestures[evType]) {
-    _add(node, evType, handler);
-    return true;
-  }
-  return false;
-}
-
-/**
- * Removes an event listener from a node for the given gesture type.
- *
- * @param {!EventTarget} node Node to remove listener from
- * @param {string} evType Gesture type: `down`, `up`, `track`, or `tap`
- * @param {!function(!Event):void} handler Event listener function previously passed to
- *  `addListener`.
- * @return {boolean} Returns true if a gesture event listener was removed.
- */
-export function removeListener(node, evType, handler) {
-  if (gestures[evType]) {
-    _remove(node, evType, handler);
-    return true;
-  }
-  return false;
-}
-
-/**
- * automate the event listeners for the native events
- *
- * @private
- * @param {!EventTarget} node Node on which to add the event.
- * @param {string} evType Event type to add.
- * @param {function(!Event)} handler Event handler function.
- * @return {void}
- */
-function _add(node, evType, handler) {
-  let recognizer = gestures[evType];
-  let deps = recognizer.deps;
-  let name = recognizer.name;
-  let gobj = node[GESTURE_KEY];
-  if (!gobj) {
-    node[GESTURE_KEY] = gobj = {};
-  }
-  for (let i = 0, dep, gd; i < deps.length; i++) {
-    dep = deps[i];
-    // don't add mouse handlers on iOS because they cause gray selection overlays
-    if (IS_TOUCH_ONLY && isMouseEvent(dep) && dep !== 'click') {
-      continue;
-    }
-    gd = gobj[dep];
-    if (!gd) {
-      gobj[dep] = gd = {_count: 0};
-    }
-    if (gd._count === 0) {
-      node.addEventListener(dep, _handleNative, PASSIVE_TOUCH(dep));
-    }
-    gd[name] = (gd[name] || 0) + 1;
-    gd._count = (gd._count || 0) + 1;
-  }
-  node.addEventListener(evType, handler);
-  if (recognizer.touchAction) {
-    setTouchAction(node, recognizer.touchAction);
-  }
-}
-
-/**
- * automate event listener removal for native events
- *
- * @private
- * @param {!EventTarget} node Node on which to remove the event.
- * @param {string} evType Event type to remove.
- * @param {function(!Event): void} handler Event handler function.
- * @return {void}
- */
-function _remove(node, evType, handler) {
-  let recognizer = gestures[evType];
-  let deps = recognizer.deps;
-  let name = recognizer.name;
-  let gobj = node[GESTURE_KEY];
-  if (gobj) {
-    for (let i = 0, dep, gd; i < deps.length; i++) {
-      dep = deps[i];
-      gd = gobj[dep];
-      if (gd && gd[name]) {
-        gd[name] = (gd[name] || 1) - 1;
-        gd._count = (gd._count || 1) - 1;
-        if (gd._count === 0) {
-          node.removeEventListener(dep, _handleNative, PASSIVE_TOUCH(dep));
-        }
-      }
-    }
-  }
-  node.removeEventListener(evType, handler);
-}
-
-/**
- * Registers a new gesture event recognizer for adding new custom
- * gesture event types.
- *
- * @param {!GestureRecognizer} recog Gesture recognizer descriptor
- * @return {void}
- */
-export function register(recog) {
-  recognizers.push(recog);
-  for (let i = 0; i < recog.emits.length; i++) {
-    gestures[recog.emits[i]] = recog;
-  }
-}
-
-/**
- * @private
- * @param {string} evName Event name.
- * @return {Object} Returns the gesture for the given event name.
- */
-function _findRecognizerByEvent(evName) {
-  for (let i = 0, r; i < recognizers.length; i++) {
-    r = recognizers[i];
-    for (let j = 0, n; j < r.emits.length; j++) {
-      n = r.emits[j];
-      if (n === evName) {
-        return r;
-      }
-    }
-  }
-  return null;
-}
-
-/**
- * Sets scrolling direction on node.
- *
- * This value is checked on first move, thus it should be called prior to
- * adding event listeners.
- *
- * @param {!EventTarget} node Node to set touch action setting on
- * @param {string} value Touch action value
- * @return {void}
- */
-export function setTouchAction(node, value) {
-  if (HAS_NATIVE_TA && node instanceof HTMLElement) {
-    // NOTE: add touchAction async so that events can be added in
-    // custom element constructors. Otherwise we run afoul of custom
-    // elements restriction against settings attributes (style) in the
-    // constructor.
-    microTask.run(() => {
-      node.style.touchAction = value;
-    });
-  }
-  node[TOUCH_ACTION] = value;
-}
-
-/**
- * Dispatches an event on the `target` element of `type` with the given
- * `detail`.
- * @private
- * @param {!EventTarget} target The element on which to fire an event.
- * @param {string} type The type of event to fire.
- * @param {!Object=} detail The detail object to populate on the event.
- * @return {void}
- */
-function _fire(target, type, detail) {
-  let ev = new Event(type, { bubbles: true, cancelable: true, composed: true });
-  ev.detail = detail;
-  wrap(/** @type {!Node} */(target)).dispatchEvent(ev);
-  // forward `preventDefault` in a clean way
-  if (ev.defaultPrevented) {
-    let preventer = detail.preventer || detail.sourceEvent;
-    if (preventer && preventer.preventDefault) {
-      preventer.preventDefault();
-    }
-  }
-}
-
-/**
- * Prevents the dispatch and default action of the given event name.
- *
- * @param {string} evName Event name.
- * @return {void}
- */
-export function prevent(evName) {
-  let recognizer = _findRecognizerByEvent(evName);
-  if (recognizer.info) {
-    recognizer.info.prevent = true;
-  }
-}
-
-/**
- * Reset the 2500ms timeout on processing mouse input after detecting touch input.
- *
- * Touch inputs create synthesized mouse inputs anywhere from 0 to 2000ms after the touch.
- * This method should only be called during testing with simulated touch inputs.
- * Calling this method in production may cause duplicate taps or other Gestures.
- *
- * @return {void}
- */
-export function resetMouseCanceller() {
-  if (POINTERSTATE.mouse.mouseIgnoreJob) {
-    POINTERSTATE.mouse.mouseIgnoreJob.flush();
-  }
-}
-
-/* eslint-disable valid-jsdoc */
-
-register({
-  name: 'downup',
-  deps: ['mousedown', 'touchstart', 'touchend'],
-  flow: {
-    start: ['mousedown', 'touchstart'],
-    end: ['mouseup', 'touchend']
-  },
-  emits: ['down', 'up'],
-
-  info: {
-    movefn: null,
-    upfn: null
-  },
-
-  /**
-   * @this {GestureRecognizer}
-   * @return {void}
-   */
-  reset: function() {
-    untrackDocument(this.info);
-  },
-
-  /**
-   * @this {GestureRecognizer}
-   * @param {MouseEvent} e
-   * @return {void}
-   */
-  mousedown: function(e) {
-    if (!hasLeftMouseButton(e)) {
-      return;
-    }
-    let t = _findOriginalTarget(e);
-    let self = this;
-    let movefn = function movefn(e) {
-      if (!hasLeftMouseButton(e)) {
-        downupFire('up', t, e);
-        untrackDocument(self.info);
-      }
-    };
-    let upfn = function upfn(e) {
-      if (hasLeftMouseButton(e)) {
-        downupFire('up', t, e);
-      }
-      untrackDocument(self.info);
-    };
-    trackDocument(this.info, movefn, upfn);
-    downupFire('down', t, e);
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {TouchEvent} e
-   * @return {void}
-   */
-  touchstart: function(e) {
-    downupFire('down', _findOriginalTarget(e), e.changedTouches[0], e);
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {TouchEvent} e
-   * @return {void}
-   */
-  touchend: function(e) {
-    downupFire('up', _findOriginalTarget(e), e.changedTouches[0], e);
-  }
-});
-
-/**
- * @param {string} type
- * @param {EventTarget} target
- * @param {Event|Touch} event
- * @param {Event=} preventer
- * @return {void}
- */
-function downupFire(type, target, event, preventer) {
-  if (!target) {
-    return;
-  }
-  _fire(target, type, {
-    x: event.clientX,
-    y: event.clientY,
-    sourceEvent: event,
-    preventer: preventer,
-    prevent: function(e) {
-      return prevent(e);
-    }
-  });
-}
-
-register({
-  name: 'track',
-  touchAction: 'none',
-  deps: ['mousedown', 'touchstart', 'touchmove', 'touchend'],
-  flow: {
-    start: ['mousedown', 'touchstart'],
-    end: ['mouseup', 'touchend']
-  },
-  emits: ['track'],
-
-  info: {
-    x: 0,
-    y: 0,
-    state: 'start',
-    started: false,
-    moves: [],
-    /** @this {GestureInfo} */
-    addMove: function(move) {
-      if (this.moves.length > TRACK_LENGTH) {
-        this.moves.shift();
-      }
-      this.moves.push(move);
-    },
-    movefn: null,
-    upfn: null,
-    prevent: false
-  },
-
-  /**
-   * @this {GestureRecognizer}
-   * @return {void}
-   */
-  reset: function() {
-    this.info.state = 'start';
-    this.info.started = false;
-    this.info.moves = [];
-    this.info.x = 0;
-    this.info.y = 0;
-    this.info.prevent = false;
-    untrackDocument(this.info);
-  },
-
-  /**
-   * @this {GestureRecognizer}
-   * @param {MouseEvent} e
-   * @return {void}
-   */
-  mousedown: function(e) {
-    if (!hasLeftMouseButton(e)) {
-      return;
-    }
-    let t = _findOriginalTarget(e);
-    let self = this;
-    let movefn = function movefn(e) {
-      let x = e.clientX, y = e.clientY;
-      if (trackHasMovedEnough(self.info, x, y)) {
-        // first move is 'start', subsequent moves are 'move', mouseup is 'end'
-        self.info.state = self.info.started ? (e.type === 'mouseup' ? 'end' : 'track') : 'start';
-        if (self.info.state === 'start') {
-          // if and only if tracking, always prevent tap
-          prevent('tap');
-        }
-        self.info.addMove({x: x, y: y});
-        if (!hasLeftMouseButton(e)) {
-          // always fire "end"
-          self.info.state = 'end';
-          untrackDocument(self.info);
-        }
-        if (t) {
-          trackFire(self.info, t, e);
-        }
-        self.info.started = true;
-      }
-    };
-    let upfn = function upfn(e) {
-      if (self.info.started) {
-        movefn(e);
-      }
-
-      // remove the temporary listeners
-      untrackDocument(self.info);
-    };
-    // add temporary document listeners as mouse retargets
-    trackDocument(this.info, movefn, upfn);
-    this.info.x = e.clientX;
-    this.info.y = e.clientY;
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {TouchEvent} e
-   * @return {void}
-   */
-  touchstart: function(e) {
-    let ct = e.changedTouches[0];
-    this.info.x = ct.clientX;
-    this.info.y = ct.clientY;
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {TouchEvent} e
-   * @return {void}
-   */
-  touchmove: function(e) {
-    let t = _findOriginalTarget(e);
-    let ct = e.changedTouches[0];
-    let x = ct.clientX, y = ct.clientY;
-    if (trackHasMovedEnough(this.info, x, y)) {
-      if (this.info.state === 'start') {
-        // if and only if tracking, always prevent tap
-        prevent('tap');
-      }
-      this.info.addMove({x: x, y: y});
-      trackFire(this.info, t, ct);
-      this.info.state = 'track';
-      this.info.started = true;
-    }
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {TouchEvent} e
-   * @return {void}
-   */
-  touchend: function(e) {
-    let t = _findOriginalTarget(e);
-    let ct = e.changedTouches[0];
-    // only trackend if track was started and not aborted
-    if (this.info.started) {
-      // reset started state on up
-      this.info.state = 'end';
-      this.info.addMove({x: ct.clientX, y: ct.clientY});
-      trackFire(this.info, t, ct);
-    }
-  }
-});
-
-/**
- * @param {!GestureInfo} info
- * @param {number} x
- * @param {number} y
- * @return {boolean}
- */
-function trackHasMovedEnough(info, x, y) {
-  if (info.prevent) {
-    return false;
-  }
-  if (info.started) {
-    return true;
-  }
-  let dx = Math.abs(info.x - x);
-  let dy = Math.abs(info.y - y);
-  return (dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE);
-}
-
-/**
- * @param {!GestureInfo} info
- * @param {?EventTarget} target
- * @param {Touch} touch
- * @return {void}
- */
-function trackFire(info, target, touch) {
-  if (!target) {
-    return;
-  }
-  let secondlast = info.moves[info.moves.length - 2];
-  let lastmove = info.moves[info.moves.length - 1];
-  let dx = lastmove.x - info.x;
-  let dy = lastmove.y - info.y;
-  let ddx, ddy = 0;
-  if (secondlast) {
-    ddx = lastmove.x - secondlast.x;
-    ddy = lastmove.y - secondlast.y;
-  }
-  _fire(target, 'track', {
-    state: info.state,
-    x: touch.clientX,
-    y: touch.clientY,
-    dx: dx,
-    dy: dy,
-    ddx: ddx,
-    ddy: ddy,
-    sourceEvent: touch,
-    hover: function() {
-      return deepTargetFind(touch.clientX, touch.clientY);
-    }
-  });
-}
-
-register({
-  name: 'tap',
-  deps: ['mousedown', 'click', 'touchstart', 'touchend'],
-  flow: {
-    start: ['mousedown', 'touchstart'],
-    end: ['click', 'touchend']
-  },
-  emits: ['tap'],
-  info: {
-    x: NaN,
-    y: NaN,
-    prevent: false
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @return {void}
-   */
-  reset: function() {
-    this.info.x = NaN;
-    this.info.y = NaN;
-    this.info.prevent = false;
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {MouseEvent} e
-   * @return {void}
-   */
-  mousedown: function(e) {
-    if (hasLeftMouseButton(e)) {
-      this.info.x = e.clientX;
-      this.info.y = e.clientY;
-    }
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {MouseEvent} e
-   * @return {void}
-   */
-  click: function(e) {
-    if (hasLeftMouseButton(e)) {
-      trackForward(this.info, e);
-    }
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {TouchEvent} e
-   * @return {void}
-   */
-  touchstart: function(e) {
-    const touch = e.changedTouches[0];
-    this.info.x = touch.clientX;
-    this.info.y = touch.clientY;
-  },
-  /**
-   * @this {GestureRecognizer}
-   * @param {TouchEvent} e
-   * @return {void}
-   */
-  touchend: function(e) {
-    trackForward(this.info, e.changedTouches[0], e);
-  }
-});
-
-/**
- * @param {!GestureInfo} info
- * @param {Event | Touch} e
- * @param {Event=} preventer
- * @return {void}
- */
-function trackForward(info, e, preventer) {
-  let dx = Math.abs(e.clientX - info.x);
-  let dy = Math.abs(e.clientY - info.y);
-  // find original target from `preventer` for TouchEvents, or `e` for MouseEvents
-  let t = _findOriginalTarget((preventer || e));
-  if (!t || (canBeDisabled[/** @type {!HTMLElement} */(t).localName] && t.hasAttribute('disabled'))) {
-    return;
-  }
-  // dx,dy can be NaN if `click` has been simulated and there was no `down` for `start`
-  if (isNaN(dx) || isNaN(dy) || (dx <= TAP_DISTANCE && dy <= TAP_DISTANCE) || isSyntheticClick(e)) {
-    // prevent taps from being generated if an event has canceled them
-    if (!info.prevent) {
-      _fire(t, 'tap', {
-        x: e.clientX,
-        y: e.clientY,
-        sourceEvent: e,
-        preventer: preventer
-      });
-    }
-  }
-}
-
-/* eslint-enable valid-jsdoc */
-
-/** @deprecated */
-export const findOriginalTarget = _findOriginalTarget;
-
-/** @deprecated */
-export const add = addListener;
-
-/** @deprecated */
-export const remove = removeListener;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/html-tag.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/html-tag.js
deleted file mode 100644
index 3b0e3de..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/html-tag.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-/**
- * Class representing a static string value which can be used to filter
- * strings by asseting that they have been created via this class. The
- * `value` property returns the string passed to the constructor.
- */
-class LiteralString {
-  constructor(string) {
-    /** @type {string} */
-    this.value = string.toString();
-  }
-  /**
-   * @return {string} LiteralString string value
-   * @override
-   */
-  toString() {
-    return this.value;
-  }
-}
-
-/**
- * @param {*} value Object to stringify into HTML
- * @return {string} HTML stringified form of `obj`
- */
-function literalValue(value) {
-  if (value instanceof LiteralString) {
-    return /** @type {!LiteralString} */(value).value;
-  } else {
-    throw new Error(
-        `non-literal value passed to Polymer's htmlLiteral function: ${value}`
-    );
-  }
-}
-
-/**
- * @param {*} value Object to stringify into HTML
- * @return {string} HTML stringified form of `obj`
- */
-function htmlValue(value) {
-  if (value instanceof HTMLTemplateElement) {
-    return /** @type {!HTMLTemplateElement } */(value).innerHTML;
-  } else if (value instanceof LiteralString) {
-    return literalValue(value);
-  } else {
-    throw new Error(
-        `non-template value passed to Polymer's html function: ${value}`);
-  }
-}
-
-/**
- * A template literal tag that creates an HTML <template> element from the
- * contents of the string.
- *
- * This allows you to write a Polymer Template in JavaScript.
- *
- * Templates can be composed by interpolating `HTMLTemplateElement`s in
- * expressions in the JavaScript template literal. The nested template's
- * `innerHTML` is included in the containing template.  The only other
- * values allowed in expressions are those returned from `htmlLiteral`
- * which ensures only literal values from JS source ever reach the HTML, to
- * guard against XSS risks.
- *
- * All other values are disallowed in expressions to help prevent XSS
- * attacks; however, `htmlLiteral` can be used to compose static
- * string values into templates. This is useful to compose strings into
- * places that do not accept html, like the css text of a `style`
- * element.
- *
- * Example:
- *
- *     static get template() {
- *       return html`
- *         <style>:host{ content:"..." }</style>
- *         <div class="shadowed">${this.partialTemplate}</div>
- *         ${super.template}
- *       `;
- *     }
- *     static get partialTemplate() { return html`<span>Partial!</span>`; }
- *
- * @param {!ITemplateArray} strings Constant parts of tagged template literal
- * @param {...*} values Variable parts of tagged template literal
- * @return {!HTMLTemplateElement} Constructed HTMLTemplateElement
- */
-export const html = function html(strings, ...values) {
-  const template = /** @type {!HTMLTemplateElement} */(document.createElement('template'));
-  template.innerHTML = values.reduce((acc, v, idx) =>
-      acc + htmlValue(v) + strings[idx + 1], strings[0]);
-  return template;
-};
-
-/**
- * An html literal tag that can be used with `html` to compose.
- * a literal string.
- *
- * Example:
- *
- *     static get template() {
- *       return html`
- *         <style>
- *           :host { display: block; }
- *           ${this.styleTemplate()}
- *         </style>
- *         <div class="shadowed">${staticValue}</div>
- *         ${super.template}
- *       `;
- *     }
- *     static get styleTemplate() {
- *        return htmlLiteral`.shadowed { background: gray; }`;
- *     }
- *
- * @param {!ITemplateArray} strings Constant parts of tagged template literal
- * @param {...*} values Variable parts of tagged template literal
- * @return {!LiteralString} Constructed literal string
- */
-export const htmlLiteral = function(strings, ...values) {
-  return new LiteralString(values.reduce((acc, v, idx) =>
-      acc + literalValue(v) + strings[idx + 1], strings[0]));
-};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/mixin.js
deleted file mode 100644
index b79c7f9..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/mixin.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-// unique global id for deduping mixins.
-let dedupeId = 0;
-
-/**
- * @constructor
- * @extends {Function}
- * @private
- */
-function MixinFunction(){}
-/** @type {(WeakMap | undefined)} */
-MixinFunction.prototype.__mixinApplications;
-/** @type {(Object | undefined)} */
-MixinFunction.prototype.__mixinSet;
-
-/* eslint-disable valid-jsdoc */
-/**
- * Wraps an ES6 class expression mixin such that the mixin is only applied
- * if it has not already been applied its base argument. Also memoizes mixin
- * applications.
- *
- * @template T
- * @param {T} mixin ES6 class expression mixin to wrap
- * @return {T}
- * @suppress {invalidCasts}
- */
-export const dedupingMixin = function(mixin) {
-  let mixinApplications = /** @type {!MixinFunction} */(mixin).__mixinApplications;
-  if (!mixinApplications) {
-    mixinApplications = new WeakMap();
-    /** @type {!MixinFunction} */(mixin).__mixinApplications = mixinApplications;
-  }
-  // maintain a unique id for each mixin
-  let mixinDedupeId = dedupeId++;
-  function dedupingMixin(base) {
-    let baseSet = /** @type {!MixinFunction} */(base).__mixinSet;
-    if (baseSet && baseSet[mixinDedupeId]) {
-      return base;
-    }
-    let map = mixinApplications;
-    let extended = map.get(base);
-    if (!extended) {
-      extended = /** @type {!Function} */(mixin)(base);
-      map.set(base, extended);
-    }
-    // copy inherited mixin set from the extended class, or the base class
-    // NOTE: we avoid use of Set here because some browser (IE11)
-    // cannot extend a base Set via the constructor.
-    let mixinSet = Object.create(/** @type {!MixinFunction} */(extended).__mixinSet || baseSet || null);
-    mixinSet[mixinDedupeId] = true;
-    /** @type {!MixinFunction} */(extended).__mixinSet = mixinSet;
-    return extended;
-  }
-
-  return dedupingMixin;
-};
-/* eslint-enable valid-jsdoc */
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/path.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/path.js
deleted file mode 100644
index 52fda93..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/path.js
+++ /dev/null
@@ -1,255 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-/**
- * Module with utilities for manipulating structured data path strings.
- *
- * @summary Module with utilities for manipulating structured data path strings.
- */
-
-/**
- * Returns true if the given string is a structured data path (has dots).
- *
- * Example:
- *
- * ```
- * isPath('foo.bar.baz') // true
- * isPath('foo')         // false
- * ```
- *
- * @param {string} path Path string
- * @return {boolean} True if the string contained one or more dots
- */
-export function isPath(path) {
-  return path.indexOf('.') >= 0;
-}
-
-/**
- * Returns the root property name for the given path.
- *
- * Example:
- *
- * ```
- * root('foo.bar.baz') // 'foo'
- * root('foo')         // 'foo'
- * ```
- *
- * @param {string} path Path string
- * @return {string} Root property name
- */
-export function root(path) {
-  let dotIndex = path.indexOf('.');
-  if (dotIndex === -1) {
-    return path;
-  }
-  return path.slice(0, dotIndex);
-}
-
-/**
- * Given `base` is `foo.bar`, `foo` is an ancestor, `foo.bar` is not
- * Returns true if the given path is an ancestor of the base path.
- *
- * Example:
- *
- * ```
- * isAncestor('foo.bar', 'foo')         // true
- * isAncestor('foo.bar', 'foo.bar')     // false
- * isAncestor('foo.bar', 'foo.bar.baz') // false
- * ```
- *
- * @param {string} base Path string to test against.
- * @param {string} path Path string to test.
- * @return {boolean} True if `path` is an ancestor of `base`.
- */
-export function isAncestor(base, path) {
-  //     base.startsWith(path + '.');
-  return base.indexOf(path + '.') === 0;
-}
-
-/**
- * Given `base` is `foo.bar`, `foo.bar.baz` is an descendant
- *
- * Example:
- *
- * ```
- * isDescendant('foo.bar', 'foo.bar.baz') // true
- * isDescendant('foo.bar', 'foo.bar')     // false
- * isDescendant('foo.bar', 'foo')         // false
- * ```
- *
- * @param {string} base Path string to test against.
- * @param {string} path Path string to test.
- * @return {boolean} True if `path` is a descendant of `base`.
- */
-export function isDescendant(base, path) {
-  //     path.startsWith(base + '.');
-  return path.indexOf(base + '.') === 0;
-}
-
-/**
- * Replaces a previous base path with a new base path, preserving the
- * remainder of the path.
- *
- * User must ensure `path` has a prefix of `base`.
- *
- * Example:
- *
- * ```
- * translate('foo.bar', 'zot', 'foo.bar.baz') // 'zot.baz'
- * ```
- *
- * @param {string} base Current base string to remove
- * @param {string} newBase New base string to replace with
- * @param {string} path Path to translate
- * @return {string} Translated string
- */
-export function translate(base, newBase, path) {
-  return newBase + path.slice(base.length);
-}
-
-/**
- * @param {string} base Path string to test against
- * @param {string} path Path string to test
- * @return {boolean} True if `path` is equal to `base`
- */
-export function matches(base, path) {
-  return (base === path) ||
-         isAncestor(base, path) ||
-         isDescendant(base, path);
-}
-
-/**
- * Converts array-based paths to flattened path.  String-based paths
- * are returned as-is.
- *
- * Example:
- *
- * ```
- * normalize(['foo.bar', 0, 'baz'])  // 'foo.bar.0.baz'
- * normalize('foo.bar.0.baz')        // 'foo.bar.0.baz'
- * ```
- *
- * @param {string | !Array<string|number>} path Input path
- * @return {string} Flattened path
- */
-export function normalize(path) {
-  if (Array.isArray(path)) {
-    let parts = [];
-    for (let i=0; i<path.length; i++) {
-      let args = path[i].toString().split('.');
-      for (let j=0; j<args.length; j++) {
-        parts.push(args[j]);
-      }
-    }
-    return parts.join('.');
-  } else {
-    return path;
-  }
-}
-
-/**
- * Splits a path into an array of property names. Accepts either arrays
- * of path parts or strings.
- *
- * Example:
- *
- * ```
- * split(['foo.bar', 0, 'baz'])  // ['foo', 'bar', '0', 'baz']
- * split('foo.bar.0.baz')        // ['foo', 'bar', '0', 'baz']
- * ```
- *
- * @param {string | !Array<string|number>} path Input path
- * @return {!Array<string>} Array of path parts
- * @suppress {checkTypes}
- */
-export function split(path) {
-  if (Array.isArray(path)) {
-    return normalize(path).split('.');
-  }
-  return path.toString().split('.');
-}
-
-/**
- * Reads a value from a path.  If any sub-property in the path is `undefined`,
- * this method returns `undefined` (will never throw.
- *
- * @param {Object} root Object from which to dereference path from
- * @param {string | !Array<string|number>} path Path to read
- * @param {Object=} info If an object is provided to `info`, the normalized
- *  (flattened) path will be set to `info.path`.
- * @return {*} Value at path, or `undefined` if the path could not be
- *  fully dereferenced.
- */
-export function get(root, path, info) {
-  let prop = root;
-  let parts = split(path);
-  // Loop over path parts[0..n-1] and dereference
-  for (let i=0; i<parts.length; i++) {
-    if (!prop) {
-      return;
-    }
-    let part = parts[i];
-    prop = prop[part];
-  }
-  if (info) {
-    info.path = parts.join('.');
-  }
-  return prop;
-}
-
-/**
- * Sets a value to a path.  If any sub-property in the path is `undefined`,
- * this method will no-op.
- *
- * @param {Object} root Object from which to dereference path from
- * @param {string | !Array<string|number>} path Path to set
- * @param {*} value Value to set to path
- * @return {string | undefined} The normalized version of the input path
- */
-export function set(root, path, value) {
-  let prop = root;
-  let parts = split(path);
-  let last = parts[parts.length-1];
-  if (parts.length > 1) {
-    // Loop over path parts[0..n-2] and dereference
-    for (let i=0; i<parts.length-1; i++) {
-      let part = parts[i];
-      prop = prop[part];
-      if (!prop) {
-        return;
-      }
-    }
-    // Set value to object at end of path
-    prop[last] = value;
-  } else {
-    // Simple property set
-    prop[path] = value;
-  }
-  return parts.join('.');
-}
-
-/**
- * Returns true if the given string is a structured data path (has dots).
- *
- * This function is deprecated.  Use `isPath` instead.
- *
- * Example:
- *
- * ```
- * isDeep('foo.bar.baz') // true
- * isDeep('foo')         // false
- * ```
- *
- * @deprecated
- * @param {string} path Path string
- * @return {boolean} True if the string contained one or more dots
- */
-export const isDeep = isPath;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/render-status.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/render-status.js
deleted file mode 100644
index 0132699a..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/render-status.js
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/**
- * Module for scheduling flushable pre-render and post-render tasks.
- *
- * @summary Module for scheduling flushable pre-render and post-render tasks.
- */
-
-import './boot.js';
-
-let scheduled = false;
-let beforeRenderQueue = [];
-let afterRenderQueue = [];
-
-function schedule() {
-  scheduled = true;
-  // before next render
-  requestAnimationFrame(function() {
-    scheduled = false;
-    flushQueue(beforeRenderQueue);
-    // after the render
-    setTimeout(function() {
-      runQueue(afterRenderQueue);
-    });
-  });
-}
-
-function flushQueue(queue) {
-  while (queue.length) {
-    callMethod(queue.shift());
-  }
-}
-
-function runQueue(queue) {
-  for (let i=0, l=queue.length; i < l; i++) {
-    callMethod(queue.shift());
-  }
-}
-
-function callMethod(info) {
-  const context = info[0];
-  const callback = info[1];
-  const args = info[2];
-  try {
-    callback.apply(context, args);
-  } catch(e) {
-    setTimeout(() => {
-      throw e;
-    });
-  }
-}
-
-/**
- * Flushes all `beforeNextRender` tasks, followed by all `afterNextRender`
- * tasks.
- *
- * @return {void}
- */
-export function flush() {
-  while (beforeRenderQueue.length || afterRenderQueue.length) {
-    flushQueue(beforeRenderQueue);
-    flushQueue(afterRenderQueue);
-  }
-  scheduled = false;
-}
-
-
-/**
- * Enqueues a callback which will be run before the next render, at
- * `requestAnimationFrame` timing.
- *
- * This method is useful for enqueuing work that requires DOM measurement,
- * since measurement may not be reliable in custom element callbacks before
- * the first render, as well as for batching measurement tasks in general.
- *
- * Tasks in this queue may be flushed by calling `flush()`.
- *
- * @param {*} context Context object the callback function will be bound to
- * @param {function(...*):void} callback Callback function
- * @param {!Array=} args An array of arguments to call the callback function with
- * @return {void}
- */
-export function beforeNextRender(context, callback, args) {
-  if (!scheduled) {
-    schedule();
-  }
-  beforeRenderQueue.push([context, callback, args]);
-}
-
-/**
- * Enqueues a callback which will be run after the next render, equivalent
- * to one task (`setTimeout`) after the next `requestAnimationFrame`.
- *
- * This method is useful for tuning the first-render performance of an
- * element or application by deferring non-critical work until after the
- * first paint.  Typical non-render-critical work may include adding UI
- * event listeners and aria attributes.
- *
- * @param {*} context Context object the callback function will be bound to
- * @param {function(...*):void} callback Callback function
- * @param {!Array=} args An array of arguments to call the callback function with
- * @return {void}
- */
-export function afterNextRender(context, callback, args) {
-  if (!scheduled) {
-    schedule();
-  }
-  afterRenderQueue.push([context, callback, args]);
-}
-
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/resolve-url.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/resolve-url.js
deleted file mode 100644
index 06b2292e..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/resolve-url.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-let CSS_URL_RX = /(url\()([^)]*)(\))/g;
-let ABS_URL = /(^\/)|(^#)|(^[\w-\d]*:)/;
-let workingURL;
-let resolveDoc;
-/**
- * Resolves the given URL against the provided `baseUri'.
- *
- * Note that this function performs no resolution for URLs that start
- * with `/` (absolute URLs) or `#` (hash identifiers).  For general purpose
- * URL resolution, use `window.URL`.
- *
- * @param {string} url Input URL to resolve
- * @param {?string=} baseURI Base URI to resolve the URL against
- * @return {string} resolved URL
- */
-export function resolveUrl(url, baseURI) {
-  if (url && ABS_URL.test(url)) {
-    return url;
-  }
-  // Lazy feature detection.
-  if (workingURL === undefined) {
-    workingURL = false;
-    try {
-      const u = new URL('b', 'http://a');
-      u.pathname = 'c%20d';
-      workingURL = (u.href === 'http://a/c%20d');
-    } catch (e) {
-      // silently fail
-    }
-  }
-  if (!baseURI) {
-    baseURI = document.baseURI || window.location.href;
-  }
-  if (workingURL) {
-    return (new URL(url, baseURI)).href;
-  }
-  // Fallback to creating an anchor into a disconnected document.
-  if (!resolveDoc) {
-    resolveDoc = document.implementation.createHTMLDocument('temp');
-    resolveDoc.base = resolveDoc.createElement('base');
-    resolveDoc.head.appendChild(resolveDoc.base);
-    resolveDoc.anchor = resolveDoc.createElement('a');
-    resolveDoc.body.appendChild(resolveDoc.anchor);
-  }
-  resolveDoc.base.href = baseURI;
-  resolveDoc.anchor.href = url;
-  return resolveDoc.anchor.href || url;
-
-}
-
-/**
- * Resolves any relative URL's in the given CSS text against the provided
- * `ownerDocument`'s `baseURI`.
- *
- * @param {string} cssText CSS text to process
- * @param {string} baseURI Base URI to resolve the URL against
- * @return {string} Processed CSS text with resolved URL's
- */
-export function resolveCss(cssText, baseURI) {
-  return cssText.replace(CSS_URL_RX, function(m, pre, url, post) {
-    return pre + '\'' +
-      resolveUrl(url.replace(/["']/g, ''), baseURI) +
-      '\'' + post;
-  });
-}
-
-/**
- * Returns a path from a given `url`. The path includes the trailing
- * `/` from the url.
- *
- * @param {string} url Input URL to transform
- * @return {string} resolved path
- */
-export function pathFromUrl(url) {
-  return url.substring(0, url.lastIndexOf('/') + 1);
-}
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/settings.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/settings.js
deleted file mode 100644
index 6b878c4e..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/settings.js
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-import './boot.js';
-
-import { pathFromUrl } from './resolve-url.js';
-export const useShadow = !(window.ShadyDOM);
-export const useNativeCSSProperties = Boolean(!window.ShadyCSS || window.ShadyCSS.nativeCss);
-export const useNativeCustomElements = !(window.customElements.polyfillWrapFlushCallback);
-
-
-/**
- * Globally settable property that is automatically assigned to
- * `ElementMixin` instances, useful for binding in templates to
- * make URL's relative to an application's root.  Defaults to the main
- * document URL, but can be overridden by users.  It may be useful to set
- * `rootPath` to provide a stable application mount path when
- * using client side routing.
- */
-export let rootPath = pathFromUrl(document.baseURI || window.location.href);
-
-/**
- * Sets the global rootPath property used by `ElementMixin` and
- * available via `rootPath`.
- *
- * @param {string} path The new root path
- * @return {void}
- */
-export const setRootPath = function(path) {
-  rootPath = path;
-};
-
-/**
- * A global callback used to sanitize any value before inserting it into the DOM.
- * The callback signature is:
- *
- *  function sanitizeDOMValue(value, name, type, node) { ... }
- *
- * Where:
- *
- * `value` is the value to sanitize.
- * `name` is the name of an attribute or property (for example, href).
- * `type` indicates where the value is being inserted: one of property, attribute, or text.
- * `node` is the node where the value is being inserted.
- *
- * @type {(function(*,string,string,Node):*)|undefined}
- */
-export let sanitizeDOMValue = window.Polymer && window.Polymer.sanitizeDOMValue || undefined;
-
-/**
- * Sets the global sanitizeDOMValue available via this module's exported
- * `sanitizeDOMValue` variable.
- *
- * @param {(function(*,string,string,Node):*)|undefined} newSanitizeDOMValue the global sanitizeDOMValue callback
- * @return {void}
- */
-export const setSanitizeDOMValue = function(newSanitizeDOMValue) {
-  sanitizeDOMValue = newSanitizeDOMValue;
-};
-
-/**
- * Globally settable property to make Polymer Gestures use passive TouchEvent listeners when recognizing gestures.
- * When set to `true`, gestures made from touch will not be able to prevent scrolling, allowing for smoother
- * scrolling performance.
- * Defaults to `false` for backwards compatibility.
- */
-export let passiveTouchGestures = false;
-
-/**
- * Sets `passiveTouchGestures` globally for all elements using Polymer Gestures.
- *
- * @param {boolean} usePassive enable or disable passive touch gestures globally
- * @return {void}
- */
-export const setPassiveTouchGestures = function(usePassive) {
-  passiveTouchGestures = usePassive;
-};
-
-/**
- * Setting to ensure Polymer template evaluation only occurs based on tempates
- * defined in trusted script.  When true, `<dom-module>` re-registration is
- * disallowed, `<dom-bind>` is disabled, and `<dom-if>`/`<dom-repeat>`
- * templates will only evaluate in the context of a trusted element template.
- */
-export let strictTemplatePolicy = false;
-
-/**
- * Sets `strictTemplatePolicy` globally for all elements
- *
- * @param {boolean} useStrictPolicy enable or disable strict template policy
- *   globally
- * @return {void}
- */
-export const setStrictTemplatePolicy = function(useStrictPolicy) {
-  strictTemplatePolicy = useStrictPolicy;
-};
-
-/**
- * Setting to enable dom-module lookup from Polymer.Element.  By default,
- * templates must be defined in script using the `static get template()`
- * getter and the `html` tag function.  To enable legacy loading of templates
- * via dom-module, set this flag to true.
- */
-export let allowTemplateFromDomModule = false;
-
-/**
- * Sets `lookupTemplateFromDomModule` globally for all elements
- *
- * @param {boolean} allowDomModule enable or disable template lookup
- *   globally
- * @return {void}
- */
-export const setAllowTemplateFromDomModule = function(allowDomModule) {
-  allowTemplateFromDomModule = allowDomModule;
-};
-
-/**
- * Setting to skip processing style includes and re-writing urls in css styles.
- * Normally "included" styles are pulled into the element and all urls in styles
- * are re-written to be relative to the containing script url.
- * If no includes or relative urls are used in styles, these steps can be
- * skipped as an optimization.
- */
-export let legacyOptimizations = false;
-
-/**
- * Sets `legacyOptimizations` globally for all elements to enable optimizations
- * when only legacy based elements are used.
- *
- * @param {boolean} useLegacyOptimizations enable or disable legacy optimizations
- * includes and url rewriting
- * @return {void}
- */
-export const setLegacyOptimizations = function(useLegacyOptimizations) {
-  legacyOptimizations = useLegacyOptimizations;
-};
-
-/**
- * Setting to perform initial rendering synchronously when running under ShadyDOM.
- * This matches the behavior of Polymer 1.
- */
-export let syncInitialRender = false;
-
-/**
- * Sets `syncInitialRender` globally for all elements to enable synchronous
- * initial rendering.
- *
- * @param {boolean} useSyncInitialRender enable or disable synchronous initial
- * rendering globally.
- * @return {void}
- */
-export const setSyncInitialRender = function(useSyncInitialRender) {
-  syncInitialRender = useSyncInitialRender;
-};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/style-gather.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/style-gather.js
deleted file mode 100644
index b67fcaf..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/style-gather.js
+++ /dev/null
@@ -1,274 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/**
- * Module with utilities for collection CSS text from `<templates>`, external
- * stylesheets, and `dom-module`s.
- *
- * @summary Module with utilities for collection CSS text from various sources.
- */
-
-import { DomModule } from '../elements/dom-module.js';
-import { resolveCss } from './resolve-url.js';
-
-const MODULE_STYLE_LINK_SELECTOR = 'link[rel=import][type~=css]';
-const INCLUDE_ATTR = 'include';
-const SHADY_UNSCOPED_ATTR = 'shady-unscoped';
-
-/**
- * @param {string} moduleId .
- * @return {?DomModule} .
- */
-function importModule(moduleId) {
-  return /** @type {?DomModule} */(DomModule.import(moduleId));
-}
-
-function styleForImport(importDoc) {
-  // NOTE: polyfill affordance.
-  // under the HTMLImports polyfill, there will be no 'body',
-  // but the import pseudo-doc can be used directly.
-  let container = importDoc.body ? importDoc.body : importDoc;
-  const importCss = resolveCss(container.textContent,
-    importDoc.baseURI);
-  const style = document.createElement('style');
-  style.textContent = importCss;
-  return style;
-}
-
-/** @typedef {{assetpath: string}} */
-let templateWithAssetPath; // eslint-disable-line no-unused-vars
-
-
-/**
- * Returns a list of <style> elements in a space-separated list of `dom-module`s.
- *
- * @function
- * @param {string} moduleIds List of dom-module id's within which to
- * search for css.
- * @return {!Array<!HTMLStyleElement>} Array of contained <style> elements
- */
-export function stylesFromModules(moduleIds) {
- const modules = moduleIds.trim().split(/\s+/);
- const styles = [];
- for (let i=0; i < modules.length; i++) {
-   styles.push(...stylesFromModule(modules[i]));
- }
- return styles;
-}
-
-/**
- * Returns a list of <style> elements in a given `dom-module`.
- * Styles in a `dom-module` can come either from `<style>`s within the
- * first `<template>`, or else from one or more
- * `<link rel="import" type="css">` links outside the template.
- *
- * @param {string} moduleId dom-module id to gather styles from
- * @return {!Array<!HTMLStyleElement>} Array of contained styles.
- */
-export function stylesFromModule(moduleId) {
-  const m = importModule(moduleId);
-
-  if (!m) {
-    console.warn('Could not find style data in module named', moduleId);
-    return [];
-  }
-
-  if (m._styles === undefined) {
-    const styles = [];
-    // module imports: <link rel="import" type="css">
-    styles.push(..._stylesFromModuleImports(m));
-    // include css from the first template in the module
-    const template = /** @type {?HTMLTemplateElement} */(
-        m.querySelector('template'));
-    if (template) {
-      styles.push(...stylesFromTemplate(template,
-        /** @type {templateWithAssetPath} */(m).assetpath));
-    }
-
-    m._styles = styles;
-  }
-
-  return m._styles;
-}
-
-/**
- * Returns the `<style>` elements within a given template.
- *
- * @param {!HTMLTemplateElement} template Template to gather styles from
- * @param {string=} baseURI baseURI for style content
- * @return {!Array<!HTMLStyleElement>} Array of styles
- */
-export function stylesFromTemplate(template, baseURI) {
-  if (!template._styles) {
-    const styles = [];
-    // if element is a template, get content from its .content
-    const e$ = template.content.querySelectorAll('style');
-    for (let i=0; i < e$.length; i++) {
-      let e = e$[i];
-      // support style sharing by allowing styles to "include"
-      // other dom-modules that contain styling
-      let include = e.getAttribute(INCLUDE_ATTR);
-      if (include) {
-        styles.push(...stylesFromModules(include).filter(function(item, index, self) {
-          return self.indexOf(item) === index;
-        }));
-      }
-      if (baseURI) {
-        e.textContent =
-            resolveCss(e.textContent, /** @type {string} */ (baseURI));
-      }
-      styles.push(e);
-    }
-    template._styles = styles;
-  }
-  return template._styles;
-}
-
-/**
- * Returns a list of <style> elements  from stylesheets loaded via `<link rel="import" type="css">` links within the specified `dom-module`.
- *
- * @param {string} moduleId Id of `dom-module` to gather CSS from
- * @return {!Array<!HTMLStyleElement>} Array of contained styles.
- */
-export function stylesFromModuleImports(moduleId) {
- let m = importModule(moduleId);
- return m ? _stylesFromModuleImports(m) : [];
-}
-
-/**
- * @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles
- * @return {!Array<!HTMLStyleElement>} Array of contained styles
- */
-function _stylesFromModuleImports(module) {
-  const styles = [];
-  const p$ = module.querySelectorAll(MODULE_STYLE_LINK_SELECTOR);
-  for (let i=0; i < p$.length; i++) {
-    let p = p$[i];
-    if (p.import) {
-      const importDoc = p.import;
-      const unscoped = p.hasAttribute(SHADY_UNSCOPED_ATTR);
-      if (unscoped && !importDoc._unscopedStyle) {
-        const style = styleForImport(importDoc);
-        style.setAttribute(SHADY_UNSCOPED_ATTR, '');
-        importDoc._unscopedStyle = style;
-      } else if (!importDoc._style) {
-        importDoc._style = styleForImport(importDoc);
-      }
-      styles.push(unscoped ? importDoc._unscopedStyle : importDoc._style);
-    }
-  }
-  return styles;
-}
-
-/**
- *
- * Returns CSS text of styles in a space-separated list of `dom-module`s.
- * Note: This method is deprecated, use `stylesFromModules` instead.
- *
- * @deprecated
- * @param {string} moduleIds List of dom-module id's within which to
- * search for css.
- * @return {string} Concatenated CSS content from specified `dom-module`s
- */
-export function cssFromModules(moduleIds) {
- let modules = moduleIds.trim().split(/\s+/);
- let cssText = '';
- for (let i=0; i < modules.length; i++) {
-   cssText += cssFromModule(modules[i]);
- }
- return cssText;
-}
-
-/**
- * Returns CSS text of styles in a given `dom-module`.  CSS in a `dom-module`
- * can come either from `<style>`s within the first `<template>`, or else
- * from one or more `<link rel="import" type="css">` links outside the
- * template.
- *
- * Any `<styles>` processed are removed from their original location.
- * Note: This method is deprecated, use `styleFromModule` instead.
- *
- * @deprecated
- * @param {string} moduleId dom-module id to gather styles from
- * @return {string} Concatenated CSS content from specified `dom-module`
- */
-export function cssFromModule(moduleId) {
-  let m = importModule(moduleId);
-  if (m && m._cssText === undefined) {
-    // module imports: <link rel="import" type="css">
-    let cssText = _cssFromModuleImports(m);
-    // include css from the first template in the module
-    let t = /** @type {?HTMLTemplateElement} */(m.querySelector('template'));
-    if (t) {
-      cssText += cssFromTemplate(t,
-        /** @type {templateWithAssetPath} */(m).assetpath);
-    }
-    m._cssText = cssText || null;
-  }
-  if (!m) {
-    console.warn('Could not find style data in module named', moduleId);
-  }
-  return m && m._cssText || '';
-}
-
-/**
- * Returns CSS text of `<styles>` within a given template.
- *
- * Any `<styles>` processed are removed from their original location.
- * Note: This method is deprecated, use `styleFromTemplate` instead.
- *
- * @deprecated
- * @param {!HTMLTemplateElement} template Template to gather styles from
- * @param {string} baseURI Base URI to resolve the URL against
- * @return {string} Concatenated CSS content from specified template
- */
-export function cssFromTemplate(template, baseURI) {
-  let cssText = '';
-  const e$ = stylesFromTemplate(template, baseURI);
-  // if element is a template, get content from its .content
-  for (let i=0; i < e$.length; i++) {
-    let e = e$[i];
-    if (e.parentNode) {
-      e.parentNode.removeChild(e);
-    }
-    cssText += e.textContent;
-  }
-  return cssText;
-}
-
-/**
- * Returns CSS text from stylesheets loaded via `<link rel="import" type="css">`
- * links within the specified `dom-module`.
- *
- * Note: This method is deprecated, use `stylesFromModuleImports` instead.
- *
- * @deprecated
- *
- * @param {string} moduleId Id of `dom-module` to gather CSS from
- * @return {string} Concatenated CSS content from links in specified `dom-module`
- */
-export function cssFromModuleImports(moduleId) {
-  let m = importModule(moduleId);
-  return m ? _cssFromModuleImports(m) : '';
-}
-
-/**
- * @deprecated
- * @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles
- * @return {string} Concatenated CSS content from links in the dom-module
- */
-function _cssFromModuleImports(module) {
-  let cssText = '';
-  let styles = _stylesFromModuleImports(module);
-  for (let i=0; i < styles.length; i++) {
-    cssText += styles[i].textContent;
-  }
-  return cssText;
-}
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/telemetry.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/telemetry.js
deleted file mode 100644
index 5e12c3e..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/telemetry.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/**
- * Total number of Polymer element instances created.
- * @type {number}
- */
-export let instanceCount = 0;
-
-export function incrementInstanceCount() {
-  instanceCount++;
-}
-
-/**
- * Array of Polymer element classes that have been finalized.
- * @type {!Array<!PolymerElementConstructor>}
- */
-export const registrations = [];
-
-/**
- * @param {!PolymerElementConstructor} prototype Element prototype to log
- * @private
- */
-function _regLog(prototype) {
-  console.log('[' + /** @type {?} */(prototype).is + ']: registered');
-}
-
-/**
- * Registers a class prototype for telemetry purposes.
- * @param {!PolymerElementConstructor} prototype Element prototype to register
- * @protected
- */
-export function register(prototype) {
-  registrations.push(prototype);
-}
-
-/**
- * Logs all elements registered with an `is` to the console.
- * @public
- */
-export function dumpRegistrations() {
-  registrations.forEach(_regLog);
-}
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/templatize.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/templatize.js
deleted file mode 100644
index 9f05135..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/templatize.js
+++ /dev/null
@@ -1,610 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/**
- * Module for preparing and stamping instances of templates that utilize
- * Polymer's data-binding and declarative event listener features.
- *
- * Example:
- *
- *     // Get a template from somewhere, e.g. light DOM
- *     let template = this.querySelector('template');
- *     // Prepare the template
- *     let TemplateClass = Templatize.templatize(template);
- *     // Instance the template with an initial data model
- *     let instance = new TemplateClass({myProp: 'initial'});
- *     // Insert the instance's DOM somewhere, e.g. element's shadow DOM
- *     this.shadowRoot.appendChild(instance.root);
- *     // Changing a property on the instance will propagate to bindings
- *     // in the template
- *     instance.myProp = 'new value';
- *
- * The `options` dictionary passed to `templatize` allows for customizing
- * features of the generated template class, including how outer-scope host
- * properties should be forwarded into template instances, how any instance
- * properties added into the template's scope should be notified out to
- * the host, and whether the instance should be decorated as a "parent model"
- * of any event handlers.
- *
- *     // Customize property forwarding and event model decoration
- *     let TemplateClass = Templatize.templatize(template, this, {
- *       parentModel: true,
- *       forwardHostProp(property, value) {...},
- *       instanceProps: {...},
- *       notifyInstanceProp(instance, property, value) {...},
- *     });
- *
- * @summary Module for preparing and stamping instances of templates
- *   utilizing Polymer templating features.
- */
-
-import './boot.js';
-
-import { PropertyEffects } from '../mixins/property-effects.js';
-import { MutableData } from '../mixins/mutable-data.js';
-import { strictTemplatePolicy } from './settings.js';
-import { wrap } from './wrap.js';
-
-// Base class for HTMLTemplateElement extension that has property effects
-// machinery for propagating host properties to children. This is an ES5
-// class only because Babel (incorrectly) requires super() in the class
-// constructor even though no `this` is used and it returns an instance.
-let newInstance = null;
-
-/**
- * @constructor
- * @extends {HTMLTemplateElement}
- * @private
- */
-function HTMLTemplateElementExtension() { return newInstance; }
-HTMLTemplateElementExtension.prototype = Object.create(HTMLTemplateElement.prototype, {
-  constructor: {
-    value: HTMLTemplateElementExtension,
-    writable: true
-  }
-});
-
-/**
- * @constructor
- * @implements {Polymer_PropertyEffects}
- * @extends {HTMLTemplateElementExtension}
- * @private
- */
-const DataTemplate = PropertyEffects(HTMLTemplateElementExtension);
-
-/**
- * @constructor
- * @implements {Polymer_MutableData}
- * @extends {DataTemplate}
- * @private
- */
-const MutableDataTemplate = MutableData(DataTemplate);
-
-// Applies a DataTemplate subclass to a <template> instance
-function upgradeTemplate(template, constructor) {
-  newInstance = template;
-  Object.setPrototypeOf(template, constructor.prototype);
-  new constructor();
-  newInstance = null;
-}
-
-/**
- * Base class for TemplateInstance.
- * @constructor
- * @implements {Polymer_PropertyEffects}
- * @private
- */
-const templateInstanceBase = PropertyEffects(class {});
-
-/**
- * @polymer
- * @customElement
- * @appliesMixin PropertyEffects
- * @unrestricted
- */
-class TemplateInstanceBase extends templateInstanceBase {
-  constructor(props) {
-    super();
-    this._configureProperties(props);
-    /** @type {!StampedTemplate} */
-    this.root = this._stampTemplate(this.__dataHost);
-    // Save list of stamped children
-    let children = this.children = [];
-    // Polymer 1.x did not use `Polymer.dom` here so not bothering.
-    for (let n = this.root.firstChild; n; n=n.nextSibling) {
-      children.push(n);
-      n.__templatizeInstance = this;
-    }
-    if (this.__templatizeOwner &&
-      this.__templatizeOwner.__hideTemplateChildren__) {
-      this._showHideChildren(true);
-    }
-    // Flush props only when props are passed if instance props exist
-    // or when there isn't instance props.
-    let options = this.__templatizeOptions;
-    if ((props && options.instanceProps) || !options.instanceProps) {
-      this._enableProperties();
-    }
-  }
-  /**
-   * Configure the given `props` by calling `_setPendingProperty`. Also
-   * sets any properties stored in `__hostProps`.
-   * @private
-   * @param {Object} props Object of property name-value pairs to set.
-   * @return {void}
-   */
-  _configureProperties(props) {
-    let options = this.__templatizeOptions;
-    if (options.forwardHostProp) {
-      for (let hprop in this.__hostProps) {
-        this._setPendingProperty(hprop, this.__dataHost['_host_' + hprop]);
-      }
-    }
-    // Any instance props passed in the constructor will overwrite host props;
-    // normally this would be a user error but we don't specifically filter them
-    for (let iprop in props) {
-      this._setPendingProperty(iprop, props[iprop]);
-    }
-  }
-  /**
-   * Forwards a host property to this instance.  This method should be
-   * called on instances from the `options.forwardHostProp` callback
-   * to propagate changes of host properties to each instance.
-   *
-   * Note this method enqueues the change, which are flushed as a batch.
-   *
-   * @param {string} prop Property or path name
-   * @param {*} value Value of the property to forward
-   * @return {void}
-   */
-  forwardHostProp(prop, value) {
-    if (this._setPendingPropertyOrPath(prop, value, false, true)) {
-      this.__dataHost._enqueueClient(this);
-    }
-  }
-
-  /**
-   * Override point for adding custom or simulated event handling.
-   *
-   * @override
-   * @param {!Node} node Node to add event listener to
-   * @param {string} eventName Name of event
-   * @param {function(!Event):void} handler Listener function to add
-   * @return {void}
-   */
-  _addEventListenerToNode(node, eventName, handler) {
-    if (this._methodHost && this.__templatizeOptions.parentModel) {
-      // If this instance should be considered a parent model, decorate
-      // events this template instance as `model`
-      this._methodHost._addEventListenerToNode(node, eventName, (e) => {
-        e.model = this;
-        handler(e);
-      });
-    } else {
-      // Otherwise delegate to the template's host (which could be)
-      // another template instance
-      let templateHost = this.__dataHost.__dataHost;
-      if (templateHost) {
-        templateHost._addEventListenerToNode(node, eventName, handler);
-      }
-    }
-  }
-  /**
-   * Shows or hides the template instance top level child elements. For
-   * text nodes, `textContent` is removed while "hidden" and replaced when
-   * "shown."
-   * @param {boolean} hide Set to true to hide the children;
-   * set to false to show them.
-   * @return {void}
-   * @protected
-   */
-  _showHideChildren(hide) {
-    let c = this.children;
-    for (let i=0; i<c.length; i++) {
-      let n = c[i];
-      // Ignore non-changes
-      if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) {
-        if (n.nodeType === Node.TEXT_NODE) {
-          if (hide) {
-            n.__polymerTextContent__ = n.textContent;
-            n.textContent = '';
-          } else {
-            n.textContent = n.__polymerTextContent__;
-          }
-        // remove and replace slot
-        } else if (n.localName === 'slot') {
-          if (hide) {
-            n.__polymerReplaced__ = document.createComment('hidden-slot');
-            wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__, n);
-          } else {
-            const replace = n.__polymerReplaced__;
-            if (replace) {
-              wrap(wrap(replace).parentNode).replaceChild(n, replace);
-            }
-          }
-        }
-
-        else if (n.style) {
-          if (hide) {
-            n.__polymerDisplay__ = n.style.display;
-            n.style.display = 'none';
-          } else {
-            n.style.display = n.__polymerDisplay__;
-          }
-        }
-      }
-      n.__hideTemplateChildren__ = hide;
-      if (n._showHideChildren) {
-        n._showHideChildren(hide);
-      }
-    }
-  }
-  /**
-   * Overrides default property-effects implementation to intercept
-   * textContent bindings while children are "hidden" and cache in
-   * private storage for later retrieval.
-   *
-   * @override
-   * @param {!Node} node The node to set a property on
-   * @param {string} prop The property to set
-   * @param {*} value The value to set
-   * @return {void}
-   * @protected
-   */
-  _setUnmanagedPropertyToNode(node, prop, value) {
-    if (node.__hideTemplateChildren__ &&
-        node.nodeType == Node.TEXT_NODE && prop == 'textContent') {
-      node.__polymerTextContent__ = value;
-    } else {
-      super._setUnmanagedPropertyToNode(node, prop, value);
-    }
-  }
-  /**
-   * Find the parent model of this template instance.  The parent model
-   * is either another templatize instance that had option `parentModel: true`,
-   * or else the host element.
-   *
-   * @return {!Polymer_PropertyEffects} The parent model of this instance
-   */
-  get parentModel() {
-    let model = this.__parentModel;
-    if (!model) {
-      let options;
-      model = this;
-      do {
-        // A template instance's `__dataHost` is a <template>
-        // `model.__dataHost.__dataHost` is the template's host
-        model = model.__dataHost.__dataHost;
-      } while ((options = model.__templatizeOptions) && !options.parentModel);
-      this.__parentModel = model;
-    }
-    return model;
-  }
-
-  /**
-   * Stub of HTMLElement's `dispatchEvent`, so that effects that may
-   * dispatch events safely no-op.
-   *
-   * @param {Event} event Event to dispatch
-   * @return {boolean} Always true.
-   */
-   dispatchEvent(event) { // eslint-disable-line no-unused-vars
-     return true;
-  }
-}
-
-/** @type {!DataTemplate} */
-TemplateInstanceBase.prototype.__dataHost;
-/** @type {!TemplatizeOptions} */
-TemplateInstanceBase.prototype.__templatizeOptions;
-/** @type {!Polymer_PropertyEffects} */
-TemplateInstanceBase.prototype._methodHost;
-/** @type {!Object} */
-TemplateInstanceBase.prototype.__templatizeOwner;
-/** @type {!Object} */
-TemplateInstanceBase.prototype.__hostProps;
-
-/**
- * @constructor
- * @extends {TemplateInstanceBase}
- * @implements {Polymer_MutableData}
- * @private
- */
-const MutableTemplateInstanceBase = MutableData(TemplateInstanceBase);
-
-function findMethodHost(template) {
-  // Technically this should be the owner of the outermost template.
-  // In shadow dom, this is always getRootNode().host, but we can
-  // approximate this via cooperation with our dataHost always setting
-  // `_methodHost` as long as there were bindings (or id's) on this
-  // instance causing it to get a dataHost.
-  let templateHost = template.__dataHost;
-  return templateHost && templateHost._methodHost || templateHost;
-}
-
-/* eslint-disable valid-jsdoc */
-/**
- * @suppress {missingProperties} class.prototype is not defined for some reason
- */
-function createTemplatizerClass(template, templateInfo, options) {
-  /**
-   * @constructor
-   * @extends {TemplateInstanceBase}
-   */
-  let templatizerBase = options.mutableData ?
-    MutableTemplateInstanceBase : TemplateInstanceBase;
-
-  // Affordance for global mixins onto TemplatizeInstance
-  if (templatize.mixin) {
-    templatizerBase = templatize.mixin(templatizerBase);
-  }
-
-  /**
-   * Anonymous class created by the templatize
-   * @constructor
-   * @private
-   */
-  let klass = class extends templatizerBase { };
-  /** @override */
-  klass.prototype.__templatizeOptions = options;
-  klass.prototype._bindTemplate(template);
-  addNotifyEffects(klass, template, templateInfo, options);
-  return klass;
-}
-
-/**
- * @suppress {missingProperties} class.prototype is not defined for some reason
- */
-function addPropagateEffects(template, templateInfo, options) {
-  let userForwardHostProp = options.forwardHostProp;
-  if (userForwardHostProp) {
-    // Provide data API and property effects on memoized template class
-    let klass = templateInfo.templatizeTemplateClass;
-    if (!klass) {
-      /**
-       * @constructor
-       * @extends {DataTemplate}
-       */
-      let templatizedBase = options.mutableData ? MutableDataTemplate : DataTemplate;
-      /** @private */
-      klass = templateInfo.templatizeTemplateClass =
-        class TemplatizedTemplate extends templatizedBase {};
-      // Add template - >instances effects
-      // and host <- template effects
-      let hostProps = templateInfo.hostProps;
-      for (let prop in hostProps) {
-        klass.prototype._addPropertyEffect('_host_' + prop,
-          klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,
-          {fn: createForwardHostPropEffect(prop, userForwardHostProp)});
-        klass.prototype._createNotifyingProperty('_host_' + prop);
-      }
-    }
-    upgradeTemplate(template, klass);
-    // Mix any pre-bound data into __data; no need to flush this to
-    // instances since they pull from the template at instance-time
-    if (template.__dataProto) {
-      // Note, generally `__dataProto` could be chained, but it's guaranteed
-      // to not be since this is a vanilla template we just added effects to
-      Object.assign(template.__data, template.__dataProto);
-    }
-    // Clear any pending data for performance
-    template.__dataTemp = {};
-    template.__dataPending = null;
-    template.__dataOld = null;
-    template._enableProperties();
-  }
-}
-/* eslint-enable valid-jsdoc */
-
-function createForwardHostPropEffect(hostProp, userForwardHostProp) {
-  return function forwardHostProp(template, prop, props) {
-    userForwardHostProp.call(template.__templatizeOwner,
-      prop.substring('_host_'.length), props[prop]);
-  };
-}
-
-function addNotifyEffects(klass, template, templateInfo, options) {
-  let hostProps = templateInfo.hostProps || {};
-  for (let iprop in options.instanceProps) {
-    delete hostProps[iprop];
-    let userNotifyInstanceProp = options.notifyInstanceProp;
-    if (userNotifyInstanceProp) {
-      klass.prototype._addPropertyEffect(iprop,
-        klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,
-        {fn: createNotifyInstancePropEffect(iprop, userNotifyInstanceProp)});
-    }
-  }
-  if (options.forwardHostProp && template.__dataHost) {
-    for (let hprop in hostProps) {
-      klass.prototype._addPropertyEffect(hprop,
-        klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,
-        {fn: createNotifyHostPropEffect()});
-    }
-  }
-}
-
-function createNotifyInstancePropEffect(instProp, userNotifyInstanceProp) {
-  return function notifyInstanceProp(inst, prop, props) {
-    userNotifyInstanceProp.call(inst.__templatizeOwner,
-      inst, prop, props[prop]);
-  };
-}
-
-function createNotifyHostPropEffect() {
-  return function notifyHostProp(inst, prop, props) {
-    inst.__dataHost._setPendingPropertyOrPath('_host_' + prop, props[prop], true, true);
-  };
-}
-
-
-/**
- * Returns an anonymous `PropertyEffects` class bound to the
- * `<template>` provided.  Instancing the class will result in the
- * template being stamped into a document fragment stored as the instance's
- * `root` property, after which it can be appended to the DOM.
- *
- * Templates may utilize all Polymer data-binding features as well as
- * declarative event listeners.  Event listeners and inline computing
- * functions in the template will be called on the host of the template.
- *
- * The constructor returned takes a single argument dictionary of initial
- * property values to propagate into template bindings.  Additionally
- * host properties can be forwarded in, and instance properties can be
- * notified out by providing optional callbacks in the `options` dictionary.
- *
- * Valid configuration in `options` are as follows:
- *
- * - `forwardHostProp(property, value)`: Called when a property referenced
- *   in the template changed on the template's host. As this library does
- *   not retain references to templates instanced by the user, it is the
- *   templatize owner's responsibility to forward host property changes into
- *   user-stamped instances.  The `instance.forwardHostProp(property, value)`
- *    method on the generated class should be called to forward host
- *   properties into the template to prevent unnecessary property-changed
- *   notifications. Any properties referenced in the template that are not
- *   defined in `instanceProps` will be notified up to the template's host
- *   automatically.
- * - `instanceProps`: Dictionary of property names that will be added
- *   to the instance by the templatize owner.  These properties shadow any
- *   host properties, and changes within the template to these properties
- *   will result in `notifyInstanceProp` being called.
- * - `mutableData`: When `true`, the generated class will skip strict
- *   dirty-checking for objects and arrays (always consider them to be
- *   "dirty").
- * - `notifyInstanceProp(instance, property, value)`: Called when
- *   an instance property changes.  Users may choose to call `notifyPath`
- *   on e.g. the owner to notify the change.
- * - `parentModel`: When `true`, events handled by declarative event listeners
- *   (`on-event="handler"`) will be decorated with a `model` property pointing
- *   to the template instance that stamped it.  It will also be returned
- *   from `instance.parentModel` in cases where template instance nesting
- *   causes an inner model to shadow an outer model.
- *
- * All callbacks are called bound to the `owner`. Any context
- * needed for the callbacks (such as references to `instances` stamped)
- * should be stored on the `owner` such that they can be retrieved via
- * `this`.
- *
- * When `options.forwardHostProp` is declared as an option, any properties
- * referenced in the template will be automatically forwarded from the host of
- * the `<template>` to instances, with the exception of any properties listed in
- * the `options.instanceProps` object.  `instanceProps` are assumed to be
- * managed by the owner of the instances, either passed into the constructor
- * or set after the fact.  Note, any properties passed into the constructor will
- * always be set to the instance (regardless of whether they would normally
- * be forwarded from the host).
- *
- * Note that `templatize()` can be run only once for a given `<template>`.
- * Further calls will result in an error. Also, there is a special
- * behavior if the template was duplicated through a mechanism such as
- * `<dom-repeat>` or `<test-fixture>`. In this case, all calls to
- * `templatize()` return the same class for all duplicates of a template.
- * The class returned from `templatize()` is generated only once using
- * the `options` from the first call. This means that any `options`
- * provided to subsequent calls will be ignored. Therefore, it is very
- * important not to close over any variables inside the callbacks. Also,
- * arrow functions must be avoided because they bind the outer `this`.
- * Inside the callbacks, any contextual information can be accessed
- * through `this`, which points to the `owner`.
- *
- * @param {!HTMLTemplateElement} template Template to templatize
- * @param {Polymer_PropertyEffects=} owner Owner of the template instances;
- *   any optional callbacks will be bound to this owner.
- * @param {Object=} options Options dictionary (see summary for details)
- * @return {function(new:TemplateInstanceBase)} Generated class bound to the template
- *   provided
- * @suppress {invalidCasts}
- */
-export function templatize(template, owner, options) {
-  // Under strictTemplatePolicy, the templatized element must be owned
-  // by a (trusted) Polymer element, indicated by existence of _methodHost;
-  // e.g. for dom-if & dom-repeat in main document, _methodHost is null
-  if (strictTemplatePolicy && !findMethodHost(template)) {
-    throw new Error('strictTemplatePolicy: template owner not trusted');
-  }
-  options = /** @type {!TemplatizeOptions} */(options || {});
-  if (template.__templatizeOwner) {
-    throw new Error('A <template> can only be templatized once');
-  }
-  template.__templatizeOwner = owner;
-  const ctor = owner ? owner.constructor : TemplateInstanceBase;
-  let templateInfo = ctor._parseTemplate(template);
-  // Get memoized base class for the prototypical template, which
-  // includes property effects for binding template & forwarding
-  /**
-   * @constructor
-   * @extends {TemplateInstanceBase}
-   */
-  let baseClass = templateInfo.templatizeInstanceClass;
-  if (!baseClass) {
-    baseClass = createTemplatizerClass(template, templateInfo, options);
-    templateInfo.templatizeInstanceClass = baseClass;
-  }
-  // Host property forwarding must be installed onto template instance
-  addPropagateEffects(template, templateInfo, options);
-  // Subclass base class and add reference for this specific template
-  /** @private */
-  let klass = class TemplateInstance extends baseClass {};
-  /** @override */
-  klass.prototype._methodHost = findMethodHost(template);
-  /** @override */
-  klass.prototype.__dataHost = /** @type {!DataTemplate} */ (template);
-  /** @override */
-  klass.prototype.__templatizeOwner = /** @type {!Object} */ (owner);
-  /** @override */
-  klass.prototype.__hostProps = templateInfo.hostProps;
-  klass = /** @type {function(new:TemplateInstanceBase)} */(klass); //eslint-disable-line no-self-assign
-  return klass;
-}
-
-/**
- * Returns the template "model" associated with a given element, which
- * serves as the binding scope for the template instance the element is
- * contained in. A template model is an instance of
- * `TemplateInstanceBase`, and should be used to manipulate data
- * associated with this template instance.
- *
- * Example:
- *
- *   let model = modelForElement(el);
- *   if (model.index < 10) {
- *     model.set('item.checked', true);
- *   }
- *
- * @param {HTMLTemplateElement} template The model will be returned for
- *   elements stamped from this template
- * @param {Node=} node Node for which to return a template model.
- * @return {TemplateInstanceBase} Template instance representing the
- *   binding scope for the element
- */
-export function modelForElement(template, node) {
-  let model;
-  while (node) {
-    // An element with a __templatizeInstance marks the top boundary
-    // of a scope; walk up until we find one, and then ensure that
-    // its __dataHost matches `this`, meaning this dom-repeat stamped it
-    if ((model = node.__templatizeInstance)) {
-      // Found an element stamped by another template; keep walking up
-      // from its __dataHost
-      if (model.__dataHost != template) {
-        node = model.__dataHost;
-      } else {
-        return model;
-      }
-    } else {
-      // Still in a template scope, keep going up until
-      // a __templatizeInstance is found
-      node = wrap(node).parentNode;
-    }
-  }
-  return null;
-}
-
-export { TemplateInstanceBase };
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/unresolved.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/unresolved.js
deleted file mode 100644
index a7c8fc84..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/unresolved.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-function resolve() {
-  document.body.removeAttribute('unresolved');
-}
-
-if (document.readyState === 'interactive' || document.readyState === 'complete') {
-  resolve();
-} else {
-  window.addEventListener('DOMContentLoaded', resolve);
-}
-
-export {};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/wrap.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/wrap.js
deleted file mode 100644
index ece21a4e..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/wrap.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/* eslint-disable valid-jsdoc */
-/**
- * Node wrapper to ensure ShadowDOM safe operation regardless of polyfill
- * presence or mode. Note that with the introduction of `ShadyDOM.noPatch`,
- * a node wrapper must be used to access ShadowDOM API.
- * This is similar to using `Polymer.dom` but relies exclusively
- * on the presence of the ShadyDOM polyfill rather than requiring the loading
- * of legacy (Polymer.dom) API.
- * @type {function(Node):Node}
- */
-export const wrap = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] && window['ShadyDOM']['wrap']) ?
-  window['ShadyDOM']['wrap'] : (n) => n;
-
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer-element.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer-element.js
deleted file mode 100644
index 1179d68..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/polymer-element.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-import { ElementMixin, version } from './lib/mixins/element-mixin.js';
-export { html } from './lib/utils/html-tag.js';
-
-export { version };
-
-/**
- * Base class that provides the core API for Polymer's meta-programming
- * features including template stamping, data-binding, attribute deserialization,
- * and property change observation.
- *
- * @customElement
- * @polymer
- * @constructor
- * @implements {Polymer_ElementMixin}
- * @extends HTMLElement
- * @appliesMixin ElementMixin
- * @summary Custom element base class that provides the core API for Polymer's
- *   key meta-programming features including template stamping, data-binding,
- *   attribute deserialization, and property change observation
- */
-export const PolymerElement = ElementMixin(HTMLElement);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer-legacy.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer-legacy.js
deleted file mode 100644
index ffdd35e0..0000000
--- a/third_party/polymer/v3_0/components-chromium/polymer/polymer-legacy.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-import { LegacyElementMixin } from './lib/legacy/legacy-element-mixin.js';
-export { Polymer } from  './lib/legacy/polymer-fn.js';
-/* template elements */
-import './lib/legacy/templatizer-behavior.js';
-import './lib/elements/dom-bind.js';
-import './lib/elements/dom-repeat.js';
-import './lib/elements/dom-if.js';
-import './lib/elements/array-selector.js';
-/* custom-style */
-import './lib/elements/custom-style.js';
-/* bc behaviors */
-import './lib/legacy/mutable-data-behavior.js';
-/* import html-tag to export html */
-export { html } from './lib/utils/html-tag.js';
-
-// bc
-export const Base = LegacyElementMixin(HTMLElement).prototype;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js
new file mode 100644
index 0000000..5c2c4c59
--- /dev/null
+++ b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js
@@ -0,0 +1 @@
+window.JSCompiler_renameProperty=function(prop,obj){return prop};let microtaskCurrHandle=0;let microtaskLastHandle=0;let microtaskCallbacks=[];let microtaskNodeContent=0;let microtaskNode=document.createTextNode("");new window.MutationObserver(microtaskFlush).observe(microtaskNode,{characterData:true});function microtaskFlush(){const len=microtaskCallbacks.length;for(let i=0;i<len;i++){let cb=microtaskCallbacks[i];if(cb){try{cb()}catch(e){setTimeout(()=>{throw e})}}}microtaskCallbacks.splice(0,len);microtaskLastHandle+=len}const timeOut={after(delay){return{run(fn){return window.setTimeout(fn,delay)},cancel(handle){window.clearTimeout(handle)}}},run(fn,delay){return window.setTimeout(fn,delay)},cancel(handle){window.clearTimeout(handle)}};const animationFrame={run(fn){return window.requestAnimationFrame(fn)},cancel(handle){window.cancelAnimationFrame(handle)}};const idlePeriod={run(fn){return window.requestIdleCallback?window.requestIdleCallback(fn):window.setTimeout(fn,16)},cancel(handle){window.cancelIdleCallback?window.cancelIdleCallback(handle):window.clearTimeout(handle)}};const microTask={run(callback){microtaskNode.textContent=microtaskNodeContent++;microtaskCallbacks.push(callback);return microtaskCurrHandle++},cancel(handle){const idx=handle-microtaskLastHandle;if(idx>=0){if(!microtaskCallbacks[idx]){throw new Error("invalid async handle: "+handle)}microtaskCallbacks[idx]=null}}};let dedupeId=0;const dedupingMixin=function(mixin){let mixinApplications=mixin.__mixinApplications;if(!mixinApplications){mixinApplications=new WeakMap;mixin.__mixinApplications=mixinApplications}let mixinDedupeId=dedupeId++;function dedupingMixin(base){let baseSet=base.__mixinSet;if(baseSet&&baseSet[mixinDedupeId]){return base}let map=mixinApplications;let extended=map.get(base);if(!extended){extended=mixin(base);map.set(base,extended)}let mixinSet=Object.create(extended.__mixinSet||baseSet||null);mixinSet[mixinDedupeId]=true;extended.__mixinSet=mixinSet;return extended}return dedupingMixin};class Debouncer{constructor(){this._asyncModule=null;this._callback=null;this._timer=null}setConfig(asyncModule,callback){this._asyncModule=asyncModule;this._callback=callback;this._timer=this._asyncModule.run(()=>{this._timer=null;debouncerQueue.delete(this);this._callback()})}cancel(){if(this.isActive()){this._cancelAsync();debouncerQueue.delete(this)}}_cancelAsync(){if(this.isActive()){this._asyncModule.cancel(this._timer);this._timer=null}}flush(){if(this.isActive()){this.cancel();this._callback()}}isActive(){return this._timer!=null}static debounce(debouncer,asyncModule,callback){if(debouncer instanceof Debouncer){debouncer._cancelAsync()}else{debouncer=new Debouncer}debouncer.setConfig(asyncModule,callback);return debouncer}}let debouncerQueue=new Set;const enqueueDebouncer=function(debouncer){debouncerQueue.add(debouncer)};const flushDebouncers=function(){const didFlush=Boolean(debouncerQueue.size);debouncerQueue.forEach(debouncer=>{try{debouncer.flush()}catch(e){setTimeout(()=>{throw e})}});return didFlush};let CSS_URL_RX=/(url\()([^)]*)(\))/g;let ABS_URL=/(^\/)|(^#)|(^[\w-\d]*:)/;let workingURL;let resolveDoc;function resolveUrl(url,baseURI){if(url&&ABS_URL.test(url)){return url}if(workingURL===undefined){workingURL=false;try{const u=new URL("b","http://a");u.pathname="c%20d";workingURL=u.href==="http://a/c%20d"}catch(e){}}if(!baseURI){baseURI=document.baseURI||window.location.href}if(workingURL){return new URL(url,baseURI).href}if(!resolveDoc){resolveDoc=document.implementation.createHTMLDocument("temp");resolveDoc.base=resolveDoc.createElement("base");resolveDoc.head.appendChild(resolveDoc.base);resolveDoc.anchor=resolveDoc.createElement("a");resolveDoc.body.appendChild(resolveDoc.anchor)}resolveDoc.base.href=baseURI;resolveDoc.anchor.href=url;return resolveDoc.anchor.href||url}function resolveCss(cssText,baseURI){return cssText.replace(CSS_URL_RX,function(m,pre,url,post){return pre+"'"+resolveUrl(url.replace(/["']/g,""),baseURI)+"'"+post})}function pathFromUrl(url){return url.substring(0,url.lastIndexOf("/")+1)}const useShadow=!window.ShadyDOM;const useNativeCSSProperties=Boolean(!window.ShadyCSS||window.ShadyCSS.nativeCss);const useNativeCustomElements=!window.customElements.polyfillWrapFlushCallback;let rootPath=pathFromUrl(document.baseURI||window.location.href);let sanitizeDOMValue=window.Polymer&&window.Polymer.sanitizeDOMValue||undefined;let passiveTouchGestures=false;let strictTemplatePolicy=false;let allowTemplateFromDomModule=false;let legacyOptimizations=false;let syncInitialRender=false;const wrap=window["ShadyDOM"]&&window["ShadyDOM"]["noPatch"]&&window["ShadyDOM"]["wrap"]?window["ShadyDOM"]["wrap"]:n=>n;let HAS_NATIVE_TA=typeof document.head.style.touchAction==="string";let GESTURE_KEY="__polymerGestures";let HANDLED_OBJ="__polymerGesturesHandled";let TOUCH_ACTION="__polymerGesturesTouchAction";let TAP_DISTANCE=25;let TRACK_DISTANCE=5;let TRACK_LENGTH=2;let MOUSE_TIMEOUT=2500;let MOUSE_EVENTS=["mousedown","mousemove","mouseup","click"];let MOUSE_WHICH_TO_BUTTONS=[0,1,4,2];let MOUSE_HAS_BUTTONS=function(){try{return new MouseEvent("test",{buttons:1}).buttons===1}catch(e){return false}}();function isMouseEvent(name){return MOUSE_EVENTS.indexOf(name)>-1}let SUPPORTS_PASSIVE=false;(function(){try{let opts=Object.defineProperty({},"passive",{get(){SUPPORTS_PASSIVE=true}});window.addEventListener("test",null,opts);window.removeEventListener("test",null,opts)}catch(e){}})();function PASSIVE_TOUCH(eventName){if(isMouseEvent(eventName)||eventName==="touchend"){return}if(HAS_NATIVE_TA&&SUPPORTS_PASSIVE&&passiveTouchGestures){return{passive:true}}else{return}}let IS_TOUCH_ONLY=navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);const clickedLabels=[];const labellable={button:true,input:true,keygen:true,meter:true,output:true,textarea:true,progress:true,select:true};const canBeDisabled={button:true,command:true,fieldset:true,input:true,keygen:true,optgroup:true,option:true,select:true,textarea:true};function canBeLabelled(el){return labellable[el.localName]||false}function matchingLabels(el){let labels=Array.prototype.slice.call(el.labels||[]);if(!labels.length){labels=[];let root=el.getRootNode();if(el.id){let matching=root.querySelectorAll(`label[for = ${el.id}]`);for(let i=0;i<matching.length;i++){labels.push(matching[i])}}}return labels}let mouseCanceller=function(mouseEvent){let sc=mouseEvent.sourceCapabilities;if(sc&&!sc.firesTouchEvents){return}mouseEvent[HANDLED_OBJ]={skip:true};if(mouseEvent.type==="click"){let clickFromLabel=false;let path=getComposedPath(mouseEvent);for(let i=0;i<path.length;i++){if(path[i].nodeType===Node.ELEMENT_NODE){if(path[i].localName==="label"){clickedLabels.push(path[i])}else if(canBeLabelled(path[i])){let ownerLabels=matchingLabels(path[i]);for(let j=0;j<ownerLabels.length;j++){clickFromLabel=clickFromLabel||clickedLabels.indexOf(ownerLabels[j])>-1}}}if(path[i]===POINTERSTATE.mouse.target){return}}if(clickFromLabel){return}mouseEvent.preventDefault();mouseEvent.stopPropagation()}};function setupTeardownMouseCanceller(setup){let events=IS_TOUCH_ONLY?["click"]:MOUSE_EVENTS;for(let i=0,en;i<events.length;i++){en=events[i];if(setup){clickedLabels.length=0;document.addEventListener(en,mouseCanceller,true)}else{document.removeEventListener(en,mouseCanceller,true)}}}function ignoreMouse(e){if(!POINTERSTATE.mouse.mouseIgnoreJob){setupTeardownMouseCanceller(true)}let unset=function(){setupTeardownMouseCanceller();POINTERSTATE.mouse.target=null;POINTERSTATE.mouse.mouseIgnoreJob=null};POINTERSTATE.mouse.target=getComposedPath(e)[0];POINTERSTATE.mouse.mouseIgnoreJob=Debouncer.debounce(POINTERSTATE.mouse.mouseIgnoreJob,timeOut.after(MOUSE_TIMEOUT),unset)}function hasLeftMouseButton(ev){let type=ev.type;if(!isMouseEvent(type)){return false}if(type==="mousemove"){let buttons=ev.buttons===undefined?1:ev.buttons;if(ev instanceof window.MouseEvent&&!MOUSE_HAS_BUTTONS){buttons=MOUSE_WHICH_TO_BUTTONS[ev.which]||0}return Boolean(buttons&1)}else{let button=ev.button===undefined?0:ev.button;return button===0}}function isSyntheticClick(ev){if(ev.type==="click"){if(ev.detail===0){return true}let t=_findOriginalTarget(ev);if(!t.nodeType||t.nodeType!==Node.ELEMENT_NODE){return true}let bcr=t.getBoundingClientRect();let x=ev.pageX,y=ev.pageY;return!(x>=bcr.left&&x<=bcr.right&&(y>=bcr.top&&y<=bcr.bottom))}return false}let POINTERSTATE={mouse:{target:null,mouseIgnoreJob:null},touch:{x:0,y:0,id:-1,scrollDecided:false}};function firstTouchAction(ev){let ta="auto";let path=getComposedPath(ev);for(let i=0,n;i<path.length;i++){n=path[i];if(n[TOUCH_ACTION]){ta=n[TOUCH_ACTION];break}}return ta}function trackDocument(stateObj,movefn,upfn){stateObj.movefn=movefn;stateObj.upfn=upfn;document.addEventListener("mousemove",movefn);document.addEventListener("mouseup",upfn)}function untrackDocument(stateObj){document.removeEventListener("mousemove",stateObj.movefn);document.removeEventListener("mouseup",stateObj.upfn);stateObj.movefn=null;stateObj.upfn=null}document.addEventListener("touchend",ignoreMouse,SUPPORTS_PASSIVE?{passive:true}:false);const getComposedPath=window.ShadyDOM&&window.ShadyDOM.noPatch?window.ShadyDOM.composedPath:event=>event.composedPath&&event.composedPath()||[];const gestures={};const recognizers=[];function deepTargetFind(x,y){let node=document.elementFromPoint(x,y);let next=node;while(next&&next.shadowRoot&&!window.ShadyDOM){let oldNext=next;next=next.shadowRoot.elementFromPoint(x,y);if(oldNext===next){break}if(next){node=next}}return node}function _findOriginalTarget(ev){const path=getComposedPath(ev);return path.length>0?path[0]:ev.target}function _handleNative(ev){let handled;let type=ev.type;let node=ev.currentTarget;let gobj=node[GESTURE_KEY];if(!gobj){return}let gs=gobj[type];if(!gs){return}if(!ev[HANDLED_OBJ]){ev[HANDLED_OBJ]={};if(type.slice(0,5)==="touch"){ev=ev;let t=ev.changedTouches[0];if(type==="touchstart"){if(ev.touches.length===1){POINTERSTATE.touch.id=t.identifier}}if(POINTERSTATE.touch.id!==t.identifier){return}if(!HAS_NATIVE_TA){if(type==="touchstart"||type==="touchmove"){_handleTouchAction(ev)}}}}handled=ev[HANDLED_OBJ];if(handled.skip){return}for(let i=0,r;i<recognizers.length;i++){r=recognizers[i];if(gs[r.name]&&!handled[r.name]){if(r.flow&&r.flow.start.indexOf(ev.type)>-1&&r.reset){r.reset()}}}for(let i=0,r;i<recognizers.length;i++){r=recognizers[i];if(gs[r.name]&&!handled[r.name]){handled[r.name]=true;r[type](ev)}}}function _handleTouchAction(ev){let t=ev.changedTouches[0];let type=ev.type;if(type==="touchstart"){POINTERSTATE.touch.x=t.clientX;POINTERSTATE.touch.y=t.clientY;POINTERSTATE.touch.scrollDecided=false}else if(type==="touchmove"){if(POINTERSTATE.touch.scrollDecided){return}POINTERSTATE.touch.scrollDecided=true;let ta=firstTouchAction(ev);let shouldPrevent=false;let dx=Math.abs(POINTERSTATE.touch.x-t.clientX);let dy=Math.abs(POINTERSTATE.touch.y-t.clientY);if(!ev.cancelable);else if(ta==="none"){shouldPrevent=true}else if(ta==="pan-x"){shouldPrevent=dy>dx}else if(ta==="pan-y"){shouldPrevent=dx>dy}if(shouldPrevent){ev.preventDefault()}else{prevent("track")}}}function addListener(node,evType,handler){if(gestures[evType]){_add(node,evType,handler);return true}return false}function removeListener(node,evType,handler){if(gestures[evType]){_remove(node,evType,handler);return true}return false}function _add(node,evType,handler){let recognizer=gestures[evType];let deps=recognizer.deps;let name=recognizer.name;let gobj=node[GESTURE_KEY];if(!gobj){node[GESTURE_KEY]=gobj={}}for(let i=0,dep,gd;i<deps.length;i++){dep=deps[i];if(IS_TOUCH_ONLY&&isMouseEvent(dep)&&dep!=="click"){continue}gd=gobj[dep];if(!gd){gobj[dep]=gd={_count:0}}if(gd._count===0){node.addEventListener(dep,_handleNative,PASSIVE_TOUCH(dep))}gd[name]=(gd[name]||0)+1;gd._count=(gd._count||0)+1}node.addEventListener(evType,handler);if(recognizer.touchAction){setTouchAction(node,recognizer.touchAction)}}function _remove(node,evType,handler){let recognizer=gestures[evType];let deps=recognizer.deps;let name=recognizer.name;let gobj=node[GESTURE_KEY];if(gobj){for(let i=0,dep,gd;i<deps.length;i++){dep=deps[i];gd=gobj[dep];if(gd&&gd[name]){gd[name]=(gd[name]||1)-1;gd._count=(gd._count||1)-1;if(gd._count===0){node.removeEventListener(dep,_handleNative,PASSIVE_TOUCH(dep))}}}}node.removeEventListener(evType,handler)}function register(recog){recognizers.push(recog);for(let i=0;i<recog.emits.length;i++){gestures[recog.emits[i]]=recog}}function _findRecognizerByEvent(evName){for(let i=0,r;i<recognizers.length;i++){r=recognizers[i];for(let j=0,n;j<r.emits.length;j++){n=r.emits[j];if(n===evName){return r}}}return null}function setTouchAction(node,value){if(HAS_NATIVE_TA&&node instanceof HTMLElement){microTask.run(()=>{node.style.touchAction=value})}node[TOUCH_ACTION]=value}function _fire(target,type,detail){let ev=new Event(type,{bubbles:true,cancelable:true,composed:true});ev.detail=detail;wrap(target).dispatchEvent(ev);if(ev.defaultPrevented){let preventer=detail.preventer||detail.sourceEvent;if(preventer&&preventer.preventDefault){preventer.preventDefault()}}}function prevent(evName){let recognizer=_findRecognizerByEvent(evName);if(recognizer.info){recognizer.info.prevent=true}}function resetMouseCanceller(){if(POINTERSTATE.mouse.mouseIgnoreJob){POINTERSTATE.mouse.mouseIgnoreJob.flush()}}register({name:"downup",deps:["mousedown","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["down","up"],info:{movefn:null,upfn:null},reset:function(){untrackDocument(this.info)},mousedown:function(e){if(!hasLeftMouseButton(e)){return}let t=_findOriginalTarget(e);let self=this;let movefn=function movefn(e){if(!hasLeftMouseButton(e)){downupFire("up",t,e);untrackDocument(self.info)}};let upfn=function upfn(e){if(hasLeftMouseButton(e)){downupFire("up",t,e)}untrackDocument(self.info)};trackDocument(this.info,movefn,upfn);downupFire("down",t,e)},touchstart:function(e){downupFire("down",_findOriginalTarget(e),e.changedTouches[0],e)},touchend:function(e){downupFire("up",_findOriginalTarget(e),e.changedTouches[0],e)}});function downupFire(type,target,event,preventer){if(!target){return}_fire(target,type,{x:event.clientX,y:event.clientY,sourceEvent:event,preventer:preventer,prevent:function(e){return prevent(e)}})}register({name:"track",touchAction:"none",deps:["mousedown","touchstart","touchmove","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["track"],info:{x:0,y:0,state:"start",started:false,moves:[],addMove:function(move){if(this.moves.length>TRACK_LENGTH){this.moves.shift()}this.moves.push(move)},movefn:null,upfn:null,prevent:false},reset:function(){this.info.state="start";this.info.started=false;this.info.moves=[];this.info.x=0;this.info.y=0;this.info.prevent=false;untrackDocument(this.info)},mousedown:function(e){if(!hasLeftMouseButton(e)){return}let t=_findOriginalTarget(e);let self=this;let movefn=function movefn(e){let x=e.clientX,y=e.clientY;if(trackHasMovedEnough(self.info,x,y)){self.info.state=self.info.started?e.type==="mouseup"?"end":"track":"start";if(self.info.state==="start"){prevent("tap")}self.info.addMove({x:x,y:y});if(!hasLeftMouseButton(e)){self.info.state="end";untrackDocument(self.info)}if(t){trackFire(self.info,t,e)}self.info.started=true}};let upfn=function upfn(e){if(self.info.started){movefn(e)}untrackDocument(self.info)};trackDocument(this.info,movefn,upfn);this.info.x=e.clientX;this.info.y=e.clientY},touchstart:function(e){let ct=e.changedTouches[0];this.info.x=ct.clientX;this.info.y=ct.clientY},touchmove:function(e){let t=_findOriginalTarget(e);let ct=e.changedTouches[0];let x=ct.clientX,y=ct.clientY;if(trackHasMovedEnough(this.info,x,y)){if(this.info.state==="start"){prevent("tap")}this.info.addMove({x:x,y:y});trackFire(this.info,t,ct);this.info.state="track";this.info.started=true}},touchend:function(e){let t=_findOriginalTarget(e);let ct=e.changedTouches[0];if(this.info.started){this.info.state="end";this.info.addMove({x:ct.clientX,y:ct.clientY});trackFire(this.info,t,ct)}}});function trackHasMovedEnough(info,x,y){if(info.prevent){return false}if(info.started){return true}let dx=Math.abs(info.x-x);let dy=Math.abs(info.y-y);return dx>=TRACK_DISTANCE||dy>=TRACK_DISTANCE}function trackFire(info,target,touch){if(!target){return}let secondlast=info.moves[info.moves.length-2];let lastmove=info.moves[info.moves.length-1];let dx=lastmove.x-info.x;let dy=lastmove.y-info.y;let ddx,ddy=0;if(secondlast){ddx=lastmove.x-secondlast.x;ddy=lastmove.y-secondlast.y}_fire(target,"track",{state:info.state,x:touch.clientX,y:touch.clientY,dx:dx,dy:dy,ddx:ddx,ddy:ddy,sourceEvent:touch,hover:function(){return deepTargetFind(touch.clientX,touch.clientY)}})}register({name:"tap",deps:["mousedown","click","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["click","touchend"]},emits:["tap"],info:{x:NaN,y:NaN,prevent:false},reset:function(){this.info.x=NaN;this.info.y=NaN;this.info.prevent=false},mousedown:function(e){if(hasLeftMouseButton(e)){this.info.x=e.clientX;this.info.y=e.clientY}},click:function(e){if(hasLeftMouseButton(e)){trackForward(this.info,e)}},touchstart:function(e){const touch=e.changedTouches[0];this.info.x=touch.clientX;this.info.y=touch.clientY},touchend:function(e){trackForward(this.info,e.changedTouches[0],e)}});function trackForward(info,e,preventer){let dx=Math.abs(e.clientX-info.x);let dy=Math.abs(e.clientY-info.y);let t=_findOriginalTarget(preventer||e);if(!t||canBeDisabled[t.localName]&&t.hasAttribute("disabled")){return}if(isNaN(dx)||isNaN(dy)||dx<=TAP_DISTANCE&&dy<=TAP_DISTANCE||isSyntheticClick(e)){if(!info.prevent){_fire(t,"tap",{x:e.clientX,y:e.clientY,sourceEvent:e,preventer:preventer})}}}const findOriginalTarget=_findOriginalTarget;const add=addListener;const remove=removeListener;var gestures$1=Object.freeze({gestures:gestures,recognizers:recognizers,deepTargetFind:deepTargetFind,addListener:addListener,removeListener:removeListener,register:register,setTouchAction:setTouchAction,prevent:prevent,resetMouseCanceller:resetMouseCanceller,findOriginalTarget:findOriginalTarget,add:add,remove:remove});const nativeShadow=!(window["ShadyDOM"]&&window["ShadyDOM"]["inUse"]);let nativeCssVariables_;function calcCssVariables(settings){if(settings&&settings["shimcssproperties"]){nativeCssVariables_=false}else{nativeCssVariables_=nativeShadow||Boolean(!navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)&&window.CSS&&CSS.supports&&CSS.supports("box-shadow","0 0 0 var(--foo)"))}}let cssBuild;if(window.ShadyCSS&&window.ShadyCSS.cssBuild!==undefined){cssBuild=window.ShadyCSS.cssBuild}const disableRuntime=Boolean(window.ShadyCSS&&window.ShadyCSS.disableRuntime);if(window.ShadyCSS&&window.ShadyCSS.nativeCss!==undefined){nativeCssVariables_=window.ShadyCSS.nativeCss}else if(window.ShadyCSS){calcCssVariables(window.ShadyCSS);window.ShadyCSS=undefined}else{calcCssVariables(window["WebComponents"]&&window["WebComponents"]["flags"])}const nativeCssVariables=nativeCssVariables_;class StyleNode{constructor(){this["start"]=0;this["end"]=0;this["previous"]=null;this["parent"]=null;this["rules"]=null;this["parsedCssText"]="";this["cssText"]="";this["atRule"]=false;this["type"]=0;this["keyframesName"]="";this["selector"]="";this["parsedSelector"]=""}}function parse(text){text=clean(text);return parseCss(lex(text),text)}function clean(cssText){return cssText.replace(RX.comments,"").replace(RX.port,"")}function lex(text){let root=new StyleNode;root["start"]=0;root["end"]=text.length;let n=root;for(let i=0,l=text.length;i<l;i++){if(text[i]===OPEN_BRACE){if(!n["rules"]){n["rules"]=[]}let p=n;let previous=p["rules"][p["rules"].length-1]||null;n=new StyleNode;n["start"]=i+1;n["parent"]=p;n["previous"]=previous;p["rules"].push(n)}else if(text[i]===CLOSE_BRACE){n["end"]=i+1;n=n["parent"]||root}}return root}function parseCss(node,text){let t=text.substring(node["start"],node["end"]-1);node["parsedCssText"]=node["cssText"]=t.trim();if(node["parent"]){let ss=node["previous"]?node["previous"]["end"]:node["parent"]["start"];t=text.substring(ss,node["start"]-1);t=_expandUnicodeEscapes(t);t=t.replace(RX.multipleSpaces," ");t=t.substring(t.lastIndexOf(";")+1);let s=node["parsedSelector"]=node["selector"]=t.trim();node["atRule"]=s.indexOf(AT_START)===0;if(node["atRule"]){if(s.indexOf(MEDIA_START)===0){node["type"]=types.MEDIA_RULE}else if(s.match(RX.keyframesRule)){node["type"]=types.KEYFRAMES_RULE;node["keyframesName"]=node["selector"].split(RX.multipleSpaces).pop()}}else{if(s.indexOf(VAR_START)===0){node["type"]=types.MIXIN_RULE}else{node["type"]=types.STYLE_RULE}}}let r$=node["rules"];if(r$){for(let i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){parseCss(r,text)}}return node}function _expandUnicodeEscapes(s){return s.replace(/\\([0-9a-f]{1,6})\s/gi,function(){let code=arguments[1],repeat=6-code.length;while(repeat--){code="0"+code}return"\\"+code})}function stringify(node,preserveProperties,text=""){let cssText="";if(node["cssText"]||node["rules"]){let r$=node["rules"];if(r$&&!_hasMixinRules(r$)){for(let i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){cssText=stringify(r,preserveProperties,cssText)}}else{cssText=preserveProperties?node["cssText"]:removeCustomProps(node["cssText"]);cssText=cssText.trim();if(cssText){cssText="  "+cssText+"\n"}}}if(cssText){if(node["selector"]){text+=node["selector"]+" "+OPEN_BRACE+"\n"}text+=cssText;if(node["selector"]){text+=CLOSE_BRACE+"\n\n"}}return text}function _hasMixinRules(rules){let r=rules[0];return Boolean(r)&&Boolean(r["selector"])&&r["selector"].indexOf(VAR_START)===0}function removeCustomProps(cssText){cssText=removeCustomPropAssignment(cssText);return removeCustomPropApply(cssText)}function removeCustomPropAssignment(cssText){return cssText.replace(RX.customProp,"").replace(RX.mixinProp,"")}function removeCustomPropApply(cssText){return cssText.replace(RX.mixinApply,"").replace(RX.varApply,"")}const types={STYLE_RULE:1,KEYFRAMES_RULE:7,MEDIA_RULE:4,MIXIN_RULE:1e3};const OPEN_BRACE="{";const CLOSE_BRACE="}";const RX={comments:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,port:/@import[^;]*;/gim,customProp:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,mixinProp:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,mixinApply:/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,varApply:/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,keyframesRule:/^@[^\s]*keyframes/,multipleSpaces:/\s+/g};const VAR_START="--";const MEDIA_START="@media";const AT_START="@";const VAR_ASSIGN=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi;const MIXIN_MATCH=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi;const MEDIA_MATCH=/@media\s(.*)/;const styleTextSet=new Set;const scopingAttribute="shady-unscoped";function processUnscopedStyle(style){const text=style.textContent;if(!styleTextSet.has(text)){styleTextSet.add(text);const newStyle=style.cloneNode(true);document.head.appendChild(newStyle)}}function isUnscopedStyle(style){return style.hasAttribute(scopingAttribute)}function toCssText(rules,callback){if(!rules){return""}if(typeof rules==="string"){rules=parse(rules)}if(callback){forEachRule(rules,callback)}return stringify(rules,nativeCssVariables)}function rulesForStyle(style){if(!style["__cssRules"]&&style.textContent){style["__cssRules"]=parse(style.textContent)}return style["__cssRules"]||null}function forEachRule(node,styleRuleCallback,keyframesRuleCallback,onlyActiveRules){if(!node){return}let skipRules=false;let type=node["type"];if(onlyActiveRules){if(type===types.MEDIA_RULE){let matchMedia=node["selector"].match(MEDIA_MATCH);if(matchMedia){if(!window.matchMedia(matchMedia[1]).matches){skipRules=true}}}}if(type===types.STYLE_RULE){styleRuleCallback(node)}else if(keyframesRuleCallback&&type===types.KEYFRAMES_RULE){keyframesRuleCallback(node)}else if(type===types.MIXIN_RULE){skipRules=true}let r$=node["rules"];if(r$&&!skipRules){for(let i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){forEachRule(r,styleRuleCallback,keyframesRuleCallback,onlyActiveRules)}}}function findMatchingParen(text,start){let level=0;for(let i=start,l=text.length;i<l;i++){if(text[i]==="("){level++}else if(text[i]===")"){if(--level===0){return i}}}return-1}function processVariableAndFallback(str,callback){let start=str.indexOf("var(");if(start===-1){return callback(str,"","","")}let end=findMatchingParen(str,start+3);let inner=str.substring(start+4,end);let prefix=str.substring(0,start);let suffix=processVariableAndFallback(str.substring(end+1),callback);let comma=inner.indexOf(",");if(comma===-1){return callback(prefix,inner.trim(),"",suffix)}let value=inner.substring(0,comma).trim();let fallback=inner.substring(comma+1).trim();return callback(prefix,value,fallback,suffix)}const wrap$1=window["ShadyDOM"]&&window["ShadyDOM"]["wrap"]||(node=>node);function getIsExtends(element){let localName=element["localName"];let is="",typeExtension="";if(localName){if(localName.indexOf("-")>-1){is=localName}else{typeExtension=localName;is=element.getAttribute&&element.getAttribute("is")||""}}else{is=element.is;typeExtension=element.extends}return{is:is,typeExtension:typeExtension}}function gatherStyleText(element){const styleTextParts=[];const styles=element.querySelectorAll("style");for(let i=0;i<styles.length;i++){const style=styles[i];if(isUnscopedStyle(style)){if(!nativeShadow){processUnscopedStyle(style);style.parentNode.removeChild(style)}}else{styleTextParts.push(style.textContent);style.parentNode.removeChild(style)}}return styleTextParts.join("").trim()}const CSS_BUILD_ATTR="css-build";function getCssBuild(element){if(cssBuild!==undefined){return cssBuild}if(element.__cssBuild===undefined){const attrValue=element.getAttribute(CSS_BUILD_ATTR);if(attrValue){element.__cssBuild=attrValue}else{const buildComment=getBuildComment(element);if(buildComment!==""){removeBuildComment(element)}element.__cssBuild=buildComment}}return element.__cssBuild||""}function elementHasBuiltCss(element){return getCssBuild(element)!==""}function getBuildComment(element){const buildComment=element.localName==="template"?element.content.firstChild:element.firstChild;if(buildComment instanceof Comment){const commentParts=buildComment.textContent.trim().split(":");if(commentParts[0]===CSS_BUILD_ATTR){return commentParts[1]}}return""}function removeBuildComment(element){const buildComment=element.localName==="template"?element.content.firstChild:element.firstChild;buildComment.parentNode.removeChild(buildComment)}function updateNativeProperties(element,properties){for(let p in properties){if(p===null){element.style.removeProperty(p)}else{element.style.setProperty(p,properties[p])}}}function getComputedStyleValue(element,property){const value=window.getComputedStyle(element).getPropertyValue(property);if(!value){return""}else{return value.trim()}}function detectMixin(cssText){const has=MIXIN_MATCH.test(cssText)||VAR_ASSIGN.test(cssText);MIXIN_MATCH.lastIndex=0;VAR_ASSIGN.lastIndex=0;return has}const APPLY_NAME_CLEAN=/;\s*/m;const INITIAL_INHERIT=/^\s*(initial)|(inherit)\s*$/;const IMPORTANT=/\s*!important/;const MIXIN_VAR_SEP="_-_";class MixinMap{constructor(){this._map={}}set(name,props){name=name.trim();this._map[name]={properties:props,dependants:{}}}get(name){name=name.trim();return this._map[name]||null}}let invalidCallback=null;class ApplyShim{constructor(){this._currentElement=null;this._measureElement=null;this._map=new MixinMap}detectMixin(cssText){return detectMixin(cssText)}gatherStyles(template){const styleText=gatherStyleText(template.content);if(styleText){const style=document.createElement("style");style.textContent=styleText;template.content.insertBefore(style,template.content.firstChild);return style}return null}transformTemplate(template,elementName){if(template._gatheredStyle===undefined){template._gatheredStyle=this.gatherStyles(template)}const style=template._gatheredStyle;return style?this.transformStyle(style,elementName):null}transformStyle(style,elementName=""){let ast=rulesForStyle(style);this.transformRules(ast,elementName);style.textContent=toCssText(ast);return ast}transformCustomStyle(style){let ast=rulesForStyle(style);forEachRule(ast,rule=>{if(rule["selector"]===":root"){rule["selector"]="html"}this.transformRule(rule)});style.textContent=toCssText(ast);return ast}transformRules(rules,elementName){this._currentElement=elementName;forEachRule(rules,r=>{this.transformRule(r)});this._currentElement=null}transformRule(rule){rule["cssText"]=this.transformCssText(rule["parsedCssText"],rule);if(rule["selector"]===":root"){rule["selector"]=":host > *"}}transformCssText(cssText,rule){cssText=cssText.replace(VAR_ASSIGN,(matchText,propertyName,valueProperty,valueMixin)=>this._produceCssProperties(matchText,propertyName,valueProperty,valueMixin,rule));return this._consumeCssProperties(cssText,rule)}_getInitialValueForProperty(property){if(!this._measureElement){this._measureElement=document.createElement("meta");this._measureElement.setAttribute("apply-shim-measure","");this._measureElement.style.all="initial";document.head.appendChild(this._measureElement)}return window.getComputedStyle(this._measureElement).getPropertyValue(property)}_fallbacksFromPreviousRules(startRule){let topRule=startRule;while(topRule["parent"]){topRule=topRule["parent"]}const fallbacks={};let seenStartRule=false;forEachRule(topRule,r=>{seenStartRule=seenStartRule||r===startRule;if(seenStartRule){return}if(r["selector"]===startRule["selector"]){Object.assign(fallbacks,this._cssTextToMap(r["parsedCssText"]))}});return fallbacks}_consumeCssProperties(text,rule){let m=null;while(m=MIXIN_MATCH.exec(text)){let matchText=m[0];let mixinName=m[1];let idx=m.index;let applyPos=idx+matchText.indexOf("@apply");let afterApplyPos=idx+matchText.length;let textBeforeApply=text.slice(0,applyPos);let textAfterApply=text.slice(afterApplyPos);let defaults=rule?this._fallbacksFromPreviousRules(rule):{};Object.assign(defaults,this._cssTextToMap(textBeforeApply));let replacement=this._atApplyToCssProperties(mixinName,defaults);text=`${textBeforeApply}${replacement}${textAfterApply}`;MIXIN_MATCH.lastIndex=idx+replacement.length}return text}_atApplyToCssProperties(mixinName,fallbacks){mixinName=mixinName.replace(APPLY_NAME_CLEAN,"");let vars=[];let mixinEntry=this._map.get(mixinName);if(!mixinEntry){this._map.set(mixinName,{});mixinEntry=this._map.get(mixinName)}if(mixinEntry){if(this._currentElement){mixinEntry.dependants[this._currentElement]=true}let p,parts,f;const properties=mixinEntry.properties;for(p in properties){f=fallbacks&&fallbacks[p];parts=[p,": var(",mixinName,MIXIN_VAR_SEP,p];if(f){parts.push(",",f.replace(IMPORTANT,""))}parts.push(")");if(IMPORTANT.test(properties[p])){parts.push(" !important")}vars.push(parts.join(""))}}return vars.join("; ")}_replaceInitialOrInherit(property,value){let match=INITIAL_INHERIT.exec(value);if(match){if(match[1]){value=this._getInitialValueForProperty(property)}else{value="apply-shim-inherit"}}return value}_cssTextToMap(text,replaceInitialOrInherit=false){let props=text.split(";");let property,value;let out={};for(let i=0,p,sp;i<props.length;i++){p=props[i];if(p){sp=p.split(":");if(sp.length>1){property=sp[0].trim();value=sp.slice(1).join(":");if(replaceInitialOrInherit){value=this._replaceInitialOrInherit(property,value)}out[property]=value}}}return out}_invalidateMixinEntry(mixinEntry){if(!invalidCallback){return}for(let elementName in mixinEntry.dependants){if(elementName!==this._currentElement){invalidCallback(elementName)}}}_produceCssProperties(matchText,propertyName,valueProperty,valueMixin,rule){if(valueProperty){processVariableAndFallback(valueProperty,(prefix,value)=>{if(value&&this._map.get(value)){valueMixin=`@apply ${value};`}})}if(!valueMixin){return matchText}let mixinAsProperties=this._consumeCssProperties(""+valueMixin,rule);let prefix=matchText.slice(0,matchText.indexOf("--"));let mixinValues=this._cssTextToMap(mixinAsProperties,true);let combinedProps=mixinValues;let mixinEntry=this._map.get(propertyName);let oldProps=mixinEntry&&mixinEntry.properties;if(oldProps){combinedProps=Object.assign(Object.create(oldProps),mixinValues)}else{this._map.set(propertyName,combinedProps)}let out=[];let p,v;let needToInvalidate=false;for(p in combinedProps){v=mixinValues[p];if(v===undefined){v="initial"}if(oldProps&&!(p in oldProps)){needToInvalidate=true}out.push(`${propertyName}${MIXIN_VAR_SEP}${p}: ${v}`)}if(needToInvalidate){this._invalidateMixinEntry(mixinEntry)}if(mixinEntry){mixinEntry.properties=combinedProps}if(valueProperty){prefix=`${matchText};${prefix}`}return`${prefix}${out.join("; ")};`}}ApplyShim.prototype["detectMixin"]=ApplyShim.prototype.detectMixin;ApplyShim.prototype["transformStyle"]=ApplyShim.prototype.transformStyle;ApplyShim.prototype["transformCustomStyle"]=ApplyShim.prototype.transformCustomStyle;ApplyShim.prototype["transformRules"]=ApplyShim.prototype.transformRules;ApplyShim.prototype["transformRule"]=ApplyShim.prototype.transformRule;ApplyShim.prototype["transformTemplate"]=ApplyShim.prototype.transformTemplate;ApplyShim.prototype["_separator"]=MIXIN_VAR_SEP;Object.defineProperty(ApplyShim.prototype,"invalidCallback",{get(){return invalidCallback},set(cb){invalidCallback=cb}});const templateMap={};const CURRENT_VERSION="_applyShimCurrentVersion";const NEXT_VERSION="_applyShimNextVersion";const VALIDATING_VERSION="_applyShimValidatingVersion";const promise=Promise.resolve();function invalidate(elementName){let template=templateMap[elementName];if(template){invalidateTemplate(template)}}function invalidateTemplate(template){template[CURRENT_VERSION]=template[CURRENT_VERSION]||0;template[VALIDATING_VERSION]=template[VALIDATING_VERSION]||0;template[NEXT_VERSION]=(template[NEXT_VERSION]||0)+1}function templateIsValid(template){return template[CURRENT_VERSION]===template[NEXT_VERSION]}function templateIsValidating(template){return!templateIsValid(template)&&template[VALIDATING_VERSION]===template[NEXT_VERSION]}function startValidatingTemplate(template){template[VALIDATING_VERSION]=template[NEXT_VERSION];if(!template._validating){template._validating=true;promise.then(function(){template[CURRENT_VERSION]=template[NEXT_VERSION];template._validating=false})}}let readyPromise=null;let whenReady=window["HTMLImports"]&&window["HTMLImports"]["whenReady"]||null;let resolveFn;function documentWait(callback){requestAnimationFrame(function(){if(whenReady){whenReady(callback)}else{if(!readyPromise){readyPromise=new Promise(resolve=>{resolveFn=resolve});if(document.readyState==="complete"){resolveFn()}else{document.addEventListener("readystatechange",()=>{if(document.readyState==="complete"){resolveFn()}})}}readyPromise.then(function(){callback&&callback()})}})}const SEEN_MARKER="__seenByShadyCSS";const CACHED_STYLE="__shadyCSSCachedStyle";let transformFn=null;let validateFn=null;class CustomStyleInterface{constructor(){this["customStyles"]=[];this["enqueued"]=false;documentWait(()=>{if(window["ShadyCSS"]["flushCustomStyles"]){window["ShadyCSS"]["flushCustomStyles"]()}})}enqueueDocumentValidation(){if(this["enqueued"]||!validateFn){return}this["enqueued"]=true;documentWait(validateFn)}addCustomStyle(style){if(!style[SEEN_MARKER]){style[SEEN_MARKER]=true;this["customStyles"].push(style);this.enqueueDocumentValidation()}}getStyleForCustomStyle(customStyle){if(customStyle[CACHED_STYLE]){return customStyle[CACHED_STYLE]}let style;if(customStyle["getStyle"]){style=customStyle["getStyle"]()}else{style=customStyle}return style}processStyles(){const cs=this["customStyles"];for(let i=0;i<cs.length;i++){const customStyle=cs[i];if(customStyle[CACHED_STYLE]){continue}const style=this.getStyleForCustomStyle(customStyle);if(style){const styleToTransform=style["__appliedElement"]||style;if(transformFn){transformFn(styleToTransform)}customStyle[CACHED_STYLE]=styleToTransform}}return cs}}CustomStyleInterface.prototype["addCustomStyle"]=CustomStyleInterface.prototype.addCustomStyle;CustomStyleInterface.prototype["getStyleForCustomStyle"]=CustomStyleInterface.prototype.getStyleForCustomStyle;CustomStyleInterface.prototype["processStyles"]=CustomStyleInterface.prototype.processStyles;Object.defineProperties(CustomStyleInterface.prototype,{transformCallback:{get(){return transformFn},set(fn){transformFn=fn}},validateCallback:{get(){return validateFn},set(fn){let needsEnqueue=false;if(!validateFn){needsEnqueue=true}validateFn=fn;if(needsEnqueue){this.enqueueDocumentValidation()}}}});const applyShim=new ApplyShim;class ApplyShimInterface{constructor(){this.customStyleInterface=null;applyShim["invalidCallback"]=invalidate}ensure(){if(this.customStyleInterface){return}if(window.ShadyCSS.CustomStyleInterface){this.customStyleInterface=window.ShadyCSS.CustomStyleInterface;this.customStyleInterface["transformCallback"]=(style=>{applyShim.transformCustomStyle(style)});this.customStyleInterface["validateCallback"]=(()=>{requestAnimationFrame(()=>{if(this.customStyleInterface["enqueued"]){this.flushCustomStyles()}})})}}prepareTemplate(template,elementName){this.ensure();if(elementHasBuiltCss(template)){return}templateMap[elementName]=template;let ast=applyShim.transformTemplate(template,elementName);template["_styleAst"]=ast}flushCustomStyles(){this.ensure();if(!this.customStyleInterface){return}let styles=this.customStyleInterface["processStyles"]();if(!this.customStyleInterface["enqueued"]){return}for(let i=0;i<styles.length;i++){let cs=styles[i];let style=this.customStyleInterface["getStyleForCustomStyle"](cs);if(style){applyShim.transformCustomStyle(style)}}this.customStyleInterface["enqueued"]=false}styleSubtree(element,properties){this.ensure();if(properties){updateNativeProperties(element,properties)}if(element.shadowRoot){this.styleElement(element);let shadowChildren=element.shadowRoot.children||element.shadowRoot.childNodes;for(let i=0;i<shadowChildren.length;i++){this.styleSubtree(shadowChildren[i])}}else{let children=element.children||element.childNodes;for(let i=0;i<children.length;i++){this.styleSubtree(children[i])}}}styleElement(element){this.ensure();let{is:is}=getIsExtends(element);let template=templateMap[is];if(template&&elementHasBuiltCss(template)){return}if(template&&!templateIsValid(template)){if(!templateIsValidating(template)){this.prepareTemplate(template,is);startValidatingTemplate(template)}let root=element.shadowRoot;if(root){let style=root.querySelector("style");if(style){style["__cssRules"]=template["_styleAst"];style.textContent=toCssText(template["_styleAst"])}}}}styleDocument(properties){this.ensure();this.styleSubtree(document.body,properties)}}if(!window.ShadyCSS||!window.ShadyCSS.ScopingShim){const applyShimInterface=new ApplyShimInterface;let CustomStyleInterface=window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface;window.ShadyCSS={prepareTemplate(template,elementName,elementExtends){applyShimInterface.flushCustomStyles();applyShimInterface.prepareTemplate(template,elementName)},prepareTemplateStyles(template,elementName,elementExtends){window.ShadyCSS.prepareTemplate(template,elementName,elementExtends)},prepareTemplateDom(template,elementName){},styleSubtree(element,properties){applyShimInterface.flushCustomStyles();applyShimInterface.styleSubtree(element,properties)},styleElement(element){applyShimInterface.flushCustomStyles();applyShimInterface.styleElement(element)},styleDocument(properties){applyShimInterface.flushCustomStyles();applyShimInterface.styleDocument(properties)},getComputedStyleValue(element,property){return getComputedStyleValue(element,property)},flushCustomStyles(){applyShimInterface.flushCustomStyles()},nativeCss:nativeCssVariables,nativeShadow:nativeShadow,cssBuild:cssBuild,disableRuntime:disableRuntime};if(CustomStyleInterface){window.ShadyCSS.CustomStyleInterface=CustomStyleInterface}}window.ShadyCSS.ApplyShim=applyShim;let modules={};let lcModules={};function setModule(id,module){modules[id]=lcModules[id.toLowerCase()]=module}function findModule(id){return modules[id]||lcModules[id.toLowerCase()]}function styleOutsideTemplateCheck(inst){if(inst.querySelector("style")){console.warn("dom-module %s has style outside template",inst.id)}}class DomModule extends HTMLElement{static get observedAttributes(){return["id"]}static import(id,selector){if(id){let m=findModule(id);if(m&&selector){return m.querySelector(selector)}return m}return null}attributeChangedCallback(name,old,value,namespace){if(old!==value){this.register()}}get assetpath(){if(!this.__assetpath){const owner=window.HTMLImports&&HTMLImports.importForElement?HTMLImports.importForElement(this)||document:this.ownerDocument;const url=resolveUrl(this.getAttribute("assetpath")||"",owner.baseURI);this.__assetpath=pathFromUrl(url)}return this.__assetpath}register(id){id=id||this.id;if(id){if(strictTemplatePolicy&&findModule(id)!==undefined){setModule(id,null);throw new Error(`strictTemplatePolicy: dom-module ${id} re-registered`)}this.id=id;setModule(id,this);styleOutsideTemplateCheck(this)}}}DomModule.prototype["modules"]=modules;customElements.define("dom-module",DomModule);const MODULE_STYLE_LINK_SELECTOR="link[rel=import][type~=css]";const INCLUDE_ATTR="include";const SHADY_UNSCOPED_ATTR="shady-unscoped";function importModule(moduleId){return DomModule.import(moduleId)}function styleForImport(importDoc){let container=importDoc.body?importDoc.body:importDoc;const importCss=resolveCss(container.textContent,importDoc.baseURI);const style=document.createElement("style");style.textContent=importCss;return style}function stylesFromModules(moduleIds){const modules=moduleIds.trim().split(/\s+/);const styles=[];for(let i=0;i<modules.length;i++){styles.push(...stylesFromModule(modules[i]))}return styles}function stylesFromModule(moduleId){const m=importModule(moduleId);if(!m){console.warn("Could not find style data in module named",moduleId);return[]}if(m._styles===undefined){const styles=[];styles.push(..._stylesFromModuleImports(m));const template=m.querySelector("template");if(template){styles.push(...stylesFromTemplate(template,m.assetpath))}m._styles=styles}return m._styles}function stylesFromTemplate(template,baseURI){if(!template._styles){const styles=[];const e$=template.content.querySelectorAll("style");for(let i=0;i<e$.length;i++){let e=e$[i];let include=e.getAttribute(INCLUDE_ATTR);if(include){styles.push(...stylesFromModules(include).filter(function(item,index,self){return self.indexOf(item)===index}))}if(baseURI){e.textContent=resolveCss(e.textContent,baseURI)}styles.push(e)}template._styles=styles}return template._styles}function stylesFromModuleImports(moduleId){let m=importModule(moduleId);return m?_stylesFromModuleImports(m):[]}function _stylesFromModuleImports(module){const styles=[];const p$=module.querySelectorAll(MODULE_STYLE_LINK_SELECTOR);for(let i=0;i<p$.length;i++){let p=p$[i];if(p.import){const importDoc=p.import;const unscoped=p.hasAttribute(SHADY_UNSCOPED_ATTR);if(unscoped&&!importDoc._unscopedStyle){const style=styleForImport(importDoc);style.setAttribute(SHADY_UNSCOPED_ATTR,"");importDoc._unscopedStyle=style}else if(!importDoc._style){importDoc._style=styleForImport(importDoc)}styles.push(unscoped?importDoc._unscopedStyle:importDoc._style)}}return styles}function cssFromModules(moduleIds){let modules=moduleIds.trim().split(/\s+/);let cssText="";for(let i=0;i<modules.length;i++){cssText+=cssFromModule(modules[i])}return cssText}function cssFromModule(moduleId){let m=importModule(moduleId);if(m&&m._cssText===undefined){let cssText=_cssFromModuleImports(m);let t=m.querySelector("template");if(t){cssText+=cssFromTemplate(t,m.assetpath)}m._cssText=cssText||null}if(!m){console.warn("Could not find style data in module named",moduleId)}return m&&m._cssText||""}function cssFromTemplate(template,baseURI){let cssText="";const e$=stylesFromTemplate(template,baseURI);for(let i=0;i<e$.length;i++){let e=e$[i];if(e.parentNode){e.parentNode.removeChild(e)}cssText+=e.textContent}return cssText}function _cssFromModuleImports(module){let cssText="";let styles=_stylesFromModuleImports(module);for(let i=0;i<styles.length;i++){cssText+=styles[i].textContent}return cssText}function isPath(path){return path.indexOf(".")>=0}function root(path){let dotIndex=path.indexOf(".");if(dotIndex===-1){return path}return path.slice(0,dotIndex)}function isAncestor(base,path){return base.indexOf(path+".")===0}function isDescendant(base,path){return path.indexOf(base+".")===0}function translate(base,newBase,path){return newBase+path.slice(base.length)}function matches(base,path){return base===path||isAncestor(base,path)||isDescendant(base,path)}function normalize(path){if(Array.isArray(path)){let parts=[];for(let i=0;i<path.length;i++){let args=path[i].toString().split(".");for(let j=0;j<args.length;j++){parts.push(args[j])}}return parts.join(".")}else{return path}}function split(path){if(Array.isArray(path)){return normalize(path).split(".")}return path.toString().split(".")}function get(root,path,info){let prop=root;let parts=split(path);for(let i=0;i<parts.length;i++){if(!prop){return}let part=parts[i];prop=prop[part]}if(info){info.path=parts.join(".")}return prop}function set(root,path,value){let prop=root;let parts=split(path);let last=parts[parts.length-1];if(parts.length>1){for(let i=0;i<parts.length-1;i++){let part=parts[i];prop=prop[part];if(!prop){return}}prop[last]=value}else{prop[path]=value}return parts.join(".")}const caseMap={};const DASH_TO_CAMEL=/-[a-z]/g;const CAMEL_TO_DASH=/([A-Z])/g;function dashToCamelCase(dash){return caseMap[dash]||(caseMap[dash]=dash.indexOf("-")<0?dash:dash.replace(DASH_TO_CAMEL,m=>m[1].toUpperCase()))}function camelToDashCase(camel){return caseMap[camel]||(caseMap[camel]=camel.replace(CAMEL_TO_DASH,"-$1").toLowerCase())}const microtask=microTask;const PropertiesChanged=dedupingMixin(superClass=>{class PropertiesChanged extends superClass{static createProperties(props){const proto=this.prototype;for(let prop in props){if(!(prop in proto)){proto._createPropertyAccessor(prop)}}}static attributeNameForProperty(property){return property.toLowerCase()}static typeForProperty(name){}_createPropertyAccessor(property,readOnly){this._addPropertyToAttributeMap(property);if(!this.hasOwnProperty("__dataHasAccessor")){this.__dataHasAccessor=Object.assign({},this.__dataHasAccessor)}if(!this.__dataHasAccessor[property]){this.__dataHasAccessor[property]=true;this._definePropertyAccessor(property,readOnly)}}_addPropertyToAttributeMap(property){if(!this.hasOwnProperty("__dataAttributes")){this.__dataAttributes=Object.assign({},this.__dataAttributes)}if(!this.__dataAttributes[property]){const attr=this.constructor.attributeNameForProperty(property);this.__dataAttributes[attr]=property}}_definePropertyAccessor(property,readOnly){Object.defineProperty(this,property,{get(){return this._getProperty(property)},set:readOnly?function(){}:function(value){this._setProperty(property,value)}})}constructor(){super();this.__dataEnabled=false;this.__dataReady=false;this.__dataInvalid=false;this.__data={};this.__dataPending=null;this.__dataOld=null;this.__dataInstanceProps=null;this.__serializing=false;this._initializeProperties()}ready(){this.__dataReady=true;this._flushProperties()}_initializeProperties(){for(let p in this.__dataHasAccessor){if(this.hasOwnProperty(p)){this.__dataInstanceProps=this.__dataInstanceProps||{};this.__dataInstanceProps[p]=this[p];delete this[p]}}}_initializeInstanceProperties(props){Object.assign(this,props)}_setProperty(property,value){if(this._setPendingProperty(property,value)){this._invalidateProperties()}}_getProperty(property){return this.__data[property]}_setPendingProperty(property,value,ext){let old=this.__data[property];let changed=this._shouldPropertyChange(property,value,old);if(changed){if(!this.__dataPending){this.__dataPending={};this.__dataOld={}}if(this.__dataOld&&!(property in this.__dataOld)){this.__dataOld[property]=old}this.__data[property]=value;this.__dataPending[property]=value}return changed}_invalidateProperties(){if(!this.__dataInvalid&&this.__dataReady){this.__dataInvalid=true;microtask.run(()=>{if(this.__dataInvalid){this.__dataInvalid=false;this._flushProperties()}})}}_enableProperties(){if(!this.__dataEnabled){this.__dataEnabled=true;if(this.__dataInstanceProps){this._initializeInstanceProperties(this.__dataInstanceProps);this.__dataInstanceProps=null}this.ready()}}_flushProperties(){const props=this.__data;const changedProps=this.__dataPending;const old=this.__dataOld;if(this._shouldPropertiesChange(props,changedProps,old)){this.__dataPending=null;this.__dataOld=null;this._propertiesChanged(props,changedProps,old)}}_shouldPropertiesChange(currentProps,changedProps,oldProps){return Boolean(changedProps)}_propertiesChanged(currentProps,changedProps,oldProps){}_shouldPropertyChange(property,value,old){return old!==value&&(old===old||value===value)}attributeChangedCallback(name,old,value,namespace){if(old!==value){this._attributeToProperty(name,value)}if(super.attributeChangedCallback){super.attributeChangedCallback(name,old,value,namespace)}}_attributeToProperty(attribute,value,type){if(!this.__serializing){const map=this.__dataAttributes;const property=map&&map[attribute]||attribute;this[property]=this._deserializeValue(value,type||this.constructor.typeForProperty(property))}}_propertyToAttribute(property,attribute,value){this.__serializing=true;value=arguments.length<3?this[property]:value;this._valueToNodeAttribute(this,value,attribute||this.constructor.attributeNameForProperty(property));this.__serializing=false}_valueToNodeAttribute(node,value,attribute){const str=this._serializeValue(value);if(str===undefined){node.removeAttribute(attribute)}else{if(attribute==="class"||attribute==="name"||attribute==="slot"){node=wrap(node)}node.setAttribute(attribute,str)}}_serializeValue(value){switch(typeof value){case"boolean":return value?"":undefined;default:return value!=null?value.toString():undefined}}_deserializeValue(value,type){switch(type){case Boolean:return value!==null;case Number:return Number(value);default:return value}}}return PropertiesChanged});const nativeProperties={};let proto=HTMLElement.prototype;while(proto){let props=Object.getOwnPropertyNames(proto);for(let i=0;i<props.length;i++){nativeProperties[props[i]]=true}proto=Object.getPrototypeOf(proto)}function saveAccessorValue(model,property){if(!nativeProperties[property]){let value=model[property];if(value!==undefined){if(model.__data){model._setPendingProperty(property,value)}else{if(!model.__dataProto){model.__dataProto={}}else if(!model.hasOwnProperty(JSCompiler_renameProperty("__dataProto",model))){model.__dataProto=Object.create(model.__dataProto)}model.__dataProto[property]=value}}}}const PropertyAccessors=dedupingMixin(superClass=>{const base=PropertiesChanged(superClass);class PropertyAccessors extends base{static createPropertiesForAttributes(){let a$=this.observedAttributes;for(let i=0;i<a$.length;i++){this.prototype._createPropertyAccessor(dashToCamelCase(a$[i]))}}static attributeNameForProperty(property){return camelToDashCase(property)}_initializeProperties(){if(this.__dataProto){this._initializeProtoProperties(this.__dataProto);this.__dataProto=null}super._initializeProperties()}_initializeProtoProperties(props){for(let p in props){this._setProperty(p,props[p])}}_ensureAttribute(attribute,value){const el=this;if(!el.hasAttribute(attribute)){this._valueToNodeAttribute(el,value,attribute)}}_serializeValue(value){switch(typeof value){case"object":if(value instanceof Date){return value.toString()}else if(value){try{return JSON.stringify(value)}catch(x){return""}}default:return super._serializeValue(value)}}_deserializeValue(value,type){let outValue;switch(type){case Object:try{outValue=JSON.parse(value)}catch(x){outValue=value}break;case Array:try{outValue=JSON.parse(value)}catch(x){outValue=null;console.warn(`Polymer::Attributes: couldn't decode Array as JSON: ${value}`)}break;case Date:outValue=isNaN(value)?String(value):Number(value);outValue=new Date(outValue);break;default:outValue=super._deserializeValue(value,type);break}return outValue}_definePropertyAccessor(property,readOnly){saveAccessorValue(this,property);super._definePropertyAccessor(property,readOnly)}_hasAccessor(property){return this.__dataHasAccessor&&this.__dataHasAccessor[property]}_isPropertyPending(prop){return Boolean(this.__dataPending&&prop in this.__dataPending)}}return PropertyAccessors});const walker=document.createTreeWalker(document,NodeFilter.SHOW_ALL,null,false);const templateExtensions={"dom-if":true,"dom-repeat":true};function wrapTemplateExtension(node){let is=node.getAttribute("is");if(is&&templateExtensions[is]){let t=node;t.removeAttribute("is");node=t.ownerDocument.createElement(is);t.parentNode.replaceChild(node,t);node.appendChild(t);while(t.attributes.length){node.setAttribute(t.attributes[0].name,t.attributes[0].value);t.removeAttribute(t.attributes[0].name)}}return node}function findTemplateNode(root,nodeInfo){let parent=nodeInfo.parentInfo&&findTemplateNode(root,nodeInfo.parentInfo);if(parent){walker.currentNode=parent;for(let n=walker.firstChild(),i=0;n;n=walker.nextSibling()){if(nodeInfo.parentIndex===i++){return n}}}else{return root}}function applyIdToMap(inst,map,node,nodeInfo){if(nodeInfo.id){map[nodeInfo.id]=node}}function applyEventListener(inst,node,nodeInfo){if(nodeInfo.events&&nodeInfo.events.length){for(let j=0,e$=nodeInfo.events,e;j<e$.length&&(e=e$[j]);j++){inst._addMethodEventListenerToNode(node,e.name,e.value,inst)}}}function applyTemplateContent(inst,node,nodeInfo){if(nodeInfo.templateInfo){node._templateInfo=nodeInfo.templateInfo}}function createNodeEventHandler(context,eventName,methodName){context=context._methodHost||context;let handler=function(e){if(context[methodName]){context[methodName](e,e.detail)}else{console.warn("listener method `"+methodName+"` not defined")}};return handler}const TemplateStamp=dedupingMixin(superClass=>{class TemplateStamp extends superClass{static _parseTemplate(template,outerTemplateInfo){if(!template._templateInfo){let templateInfo=template._templateInfo={};templateInfo.nodeInfoList=[];templateInfo.stripWhiteSpace=outerTemplateInfo&&outerTemplateInfo.stripWhiteSpace||template.hasAttribute("strip-whitespace");this._parseTemplateContent(template,templateInfo,{parent:null})}return template._templateInfo}static _parseTemplateContent(template,templateInfo,nodeInfo){return this._parseTemplateNode(template.content,templateInfo,nodeInfo)}static _parseTemplateNode(node,templateInfo,nodeInfo){let noted;let element=node;if(element.localName=="template"&&!element.hasAttribute("preserve-content")){noted=this._parseTemplateNestedTemplate(element,templateInfo,nodeInfo)||noted}else if(element.localName==="slot"){templateInfo.hasInsertionPoint=true}walker.currentNode=element;if(walker.firstChild()){noted=this._parseTemplateChildNodes(element,templateInfo,nodeInfo)||noted}if(element.hasAttributes&&element.hasAttributes()){noted=this._parseTemplateNodeAttributes(element,templateInfo,nodeInfo)||noted}return noted}static _parseTemplateChildNodes(root,templateInfo,nodeInfo){if(root.localName==="script"||root.localName==="style"){return}walker.currentNode=root;for(let node=walker.firstChild(),parentIndex=0,next;node;node=next){if(node.localName=="template"){node=wrapTemplateExtension(node)}walker.currentNode=node;next=walker.nextSibling();if(node.nodeType===Node.TEXT_NODE){let n=next;while(n&&n.nodeType===Node.TEXT_NODE){node.textContent+=n.textContent;next=walker.nextSibling();root.removeChild(n);n=next}if(templateInfo.stripWhiteSpace&&!node.textContent.trim()){root.removeChild(node);continue}}let childInfo={parentIndex:parentIndex,parentInfo:nodeInfo};if(this._parseTemplateNode(node,templateInfo,childInfo)){childInfo.infoIndex=templateInfo.nodeInfoList.push(childInfo)-1}walker.currentNode=node;if(walker.parentNode()){parentIndex++}}}static _parseTemplateNestedTemplate(node,outerTemplateInfo,nodeInfo){let templateInfo=this._parseTemplate(node,outerTemplateInfo);let content=templateInfo.content=node.content.ownerDocument.createDocumentFragment();content.appendChild(node.content);nodeInfo.templateInfo=templateInfo;return true}static _parseTemplateNodeAttributes(node,templateInfo,nodeInfo){let noted=false;let attrs=Array.from(node.attributes);for(let i=attrs.length-1,a;a=attrs[i];i--){noted=this._parseTemplateNodeAttribute(node,templateInfo,nodeInfo,a.name,a.value)||noted}return noted}static _parseTemplateNodeAttribute(node,templateInfo,nodeInfo,name,value){if(name.slice(0,3)==="on-"){node.removeAttribute(name);nodeInfo.events=nodeInfo.events||[];nodeInfo.events.push({name:name.slice(3),value:value});return true}else if(name==="id"){nodeInfo.id=value;return true}return false}static _contentForTemplate(template){let templateInfo=template._templateInfo;return templateInfo&&templateInfo.content||template.content}_stampTemplate(template){if(template&&!template.content&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate){HTMLTemplateElement.decorate(template)}let templateInfo=this.constructor._parseTemplate(template);let nodeInfo=templateInfo.nodeInfoList;let content=templateInfo.content||template.content;let dom=document.importNode(content,true);dom.__noInsertionPoint=!templateInfo.hasInsertionPoint;let nodes=dom.nodeList=new Array(nodeInfo.length);dom.$={};for(let i=0,l=nodeInfo.length,info;i<l&&(info=nodeInfo[i]);i++){let node=nodes[i]=findTemplateNode(dom,info);applyIdToMap(this,dom.$,node,info);applyTemplateContent(this,node,info);applyEventListener(this,node,info)}dom=dom;return dom}_addMethodEventListenerToNode(node,eventName,methodName,context){context=context||node;let handler=createNodeEventHandler(context,eventName,methodName);this._addEventListenerToNode(node,eventName,handler);return handler}_addEventListenerToNode(node,eventName,handler){node.addEventListener(eventName,handler)}_removeEventListenerFromNode(node,eventName,handler){node.removeEventListener(eventName,handler)}}return TemplateStamp});let dedupeId$1=0;const TYPES={COMPUTE:"__computeEffects",REFLECT:"__reflectEffects",NOTIFY:"__notifyEffects",PROPAGATE:"__propagateEffects",OBSERVE:"__observeEffects",READ_ONLY:"__readOnly"};const capitalAttributeRegex=/[A-Z]/;function ensureOwnEffectMap(model,type){let effects=model[type];if(!effects){effects=model[type]={}}else if(!model.hasOwnProperty(type)){effects=model[type]=Object.create(model[type]);for(let p in effects){let protoFx=effects[p];let instFx=effects[p]=Array(protoFx.length);for(let i=0;i<protoFx.length;i++){instFx[i]=protoFx[i]}}}return effects}function runEffects(inst,effects,props,oldProps,hasPaths,extraArgs){if(effects){let ran=false;let id=dedupeId$1++;for(let prop in props){if(runEffectsForProperty(inst,effects,id,prop,props,oldProps,hasPaths,extraArgs)){ran=true}}return ran}return false}function runEffectsForProperty(inst,effects,dedupeId,prop,props,oldProps,hasPaths,extraArgs){let ran=false;let rootProperty=hasPaths?root(prop):prop;let fxs=effects[rootProperty];if(fxs){for(let i=0,l=fxs.length,fx;i<l&&(fx=fxs[i]);i++){if((!fx.info||fx.info.lastRun!==dedupeId)&&(!hasPaths||pathMatchesTrigger(prop,fx.trigger))){if(fx.info){fx.info.lastRun=dedupeId}fx.fn(inst,prop,props,oldProps,fx.info,hasPaths,extraArgs);ran=true}}}return ran}function pathMatchesTrigger(path,trigger){if(trigger){let triggerPath=trigger.name;return triggerPath==path||!!(trigger.structured&&isAncestor(triggerPath,path))||!!(trigger.wildcard&&isDescendant(triggerPath,path))}else{return true}}function runObserverEffect(inst,property,props,oldProps,info){let fn=typeof info.method==="string"?inst[info.method]:info.method;let changedProp=info.property;if(fn){fn.call(inst,inst.__data[changedProp],oldProps[changedProp])}else if(!info.dynamicFn){console.warn("observer method `"+info.method+"` not defined")}}function runNotifyEffects(inst,notifyProps,props,oldProps,hasPaths){let fxs=inst[TYPES.NOTIFY];let notified;let id=dedupeId$1++;for(let prop in notifyProps){if(notifyProps[prop]){if(fxs&&runEffectsForProperty(inst,fxs,id,prop,props,oldProps,hasPaths)){notified=true}else if(hasPaths&&notifyPath(inst,prop,props)){notified=true}}}let host;if(notified&&(host=inst.__dataHost)&&host._invalidateProperties){host._invalidateProperties()}}function notifyPath(inst,path,props){let rootProperty=root(path);if(rootProperty!==path){let eventName=camelToDashCase(rootProperty)+"-changed";dispatchNotifyEvent(inst,eventName,props[path],path);return true}return false}function dispatchNotifyEvent(inst,eventName,value,path){let detail={value:value,queueProperty:true};if(path){detail.path=path}wrap(inst).dispatchEvent(new CustomEvent(eventName,{detail:detail}))}function runNotifyEffect(inst,property,props,oldProps,info,hasPaths){let rootProperty=hasPaths?root(property):property;let path=rootProperty!=property?property:null;let value=path?get(inst,path):inst.__data[property];if(path&&value===undefined){value=props[property]}dispatchNotifyEvent(inst,info.eventName,value,path)}function handleNotification(event,inst,fromProp,toPath,negate){let value;let detail=event.detail;let fromPath=detail&&detail.path;if(fromPath){toPath=translate(fromProp,toPath,fromPath);value=detail&&detail.value}else{value=event.currentTarget[fromProp]}value=negate?!value:value;if(!inst[TYPES.READ_ONLY]||!inst[TYPES.READ_ONLY][toPath]){if(inst._setPendingPropertyOrPath(toPath,value,true,Boolean(fromPath))&&(!detail||!detail.queueProperty)){inst._invalidateProperties()}}}function runReflectEffect(inst,property,props,oldProps,info){let value=inst.__data[property];if(sanitizeDOMValue){value=sanitizeDOMValue(value,info.attrName,"attribute",inst)}inst._propertyToAttribute(property,info.attrName,value)}function runComputedEffects(inst,changedProps,oldProps,hasPaths){let computeEffects=inst[TYPES.COMPUTE];if(computeEffects){let inputProps=changedProps;while(runEffects(inst,computeEffects,inputProps,oldProps,hasPaths)){Object.assign(oldProps,inst.__dataOld);Object.assign(changedProps,inst.__dataPending);inputProps=inst.__dataPending;inst.__dataPending=null}}}function runComputedEffect(inst,property,props,oldProps,info){let result=runMethodEffect(inst,property,props,oldProps,info);let computedProp=info.methodInfo;if(inst.__dataHasAccessor&&inst.__dataHasAccessor[computedProp]){inst._setPendingProperty(computedProp,result,true)}else{inst[computedProp]=result}}function computeLinkedPaths(inst,path,value){let links=inst.__dataLinkedPaths;if(links){let link;for(let a in links){let b=links[a];if(isDescendant(a,path)){link=translate(a,b,path);inst._setPendingPropertyOrPath(link,value,true,true)}else if(isDescendant(b,path)){link=translate(b,a,path);inst._setPendingPropertyOrPath(link,value,true,true)}}}}function addBinding(constructor,templateInfo,nodeInfo,kind,target,parts,literal){nodeInfo.bindings=nodeInfo.bindings||[];let binding={kind:kind,target:target,parts:parts,literal:literal,isCompound:parts.length!==1};nodeInfo.bindings.push(binding);if(shouldAddListener(binding)){let{event:event,negate:negate}=binding.parts[0];binding.listenerEvent=event||camelToDashCase(target)+"-changed";binding.listenerNegate=negate}let index=templateInfo.nodeInfoList.length;for(let i=0;i<binding.parts.length;i++){let part=binding.parts[i];part.compoundIndex=i;addEffectForBindingPart(constructor,templateInfo,binding,part,index)}}function addEffectForBindingPart(constructor,templateInfo,binding,part,index){if(!part.literal){if(binding.kind==="attribute"&&binding.target[0]==="-"){console.warn("Cannot set attribute "+binding.target+' because "-" is not a valid attribute starting character')}else{let dependencies=part.dependencies;let info={index:index,binding:binding,part:part,evaluator:constructor};for(let j=0;j<dependencies.length;j++){let trigger=dependencies[j];if(typeof trigger=="string"){trigger=parseArg(trigger);trigger.wildcard=true}constructor._addTemplatePropertyEffect(templateInfo,trigger.rootProperty,{fn:runBindingEffect,info:info,trigger:trigger})}}}}function runBindingEffect(inst,path,props,oldProps,info,hasPaths,nodeList){let node=nodeList[info.index];let binding=info.binding;let part=info.part;if(hasPaths&&part.source&&path.length>part.source.length&&binding.kind=="property"&&!binding.isCompound&&node.__isPropertyEffectsClient&&node.__dataHasAccessor&&node.__dataHasAccessor[binding.target]){let value=props[path];path=translate(part.source,binding.target,path);if(node._setPendingPropertyOrPath(path,value,false,true)){inst._enqueueClient(node)}}else{let value=info.evaluator._evaluateBinding(inst,part,path,props,oldProps,hasPaths);applyBindingValue(inst,node,binding,part,value)}}function applyBindingValue(inst,node,binding,part,value){value=computeBindingValue(node,value,binding,part);if(sanitizeDOMValue){value=sanitizeDOMValue(value,binding.target,binding.kind,node)}if(binding.kind=="attribute"){inst._valueToNodeAttribute(node,value,binding.target)}else{let prop=binding.target;if(node.__isPropertyEffectsClient&&node.__dataHasAccessor&&node.__dataHasAccessor[prop]){if(!node[TYPES.READ_ONLY]||!node[TYPES.READ_ONLY][prop]){if(node._setPendingProperty(prop,value)){inst._enqueueClient(node)}}}else{inst._setUnmanagedPropertyToNode(node,prop,value)}}}function computeBindingValue(node,value,binding,part){if(binding.isCompound){let storage=node.__dataCompoundStorage[binding.target];storage[part.compoundIndex]=value;value=storage.join("")}if(binding.kind!=="attribute"){if(binding.target==="textContent"||binding.target==="value"&&(node.localName==="input"||node.localName==="textarea")){value=value==undefined?"":value}}return value}function shouldAddListener(binding){return Boolean(binding.target)&&binding.kind!="attribute"&&binding.kind!="text"&&!binding.isCompound&&binding.parts[0].mode==="{"}function setupBindings(inst,templateInfo){let{nodeList:nodeList,nodeInfoList:nodeInfoList}=templateInfo;if(nodeInfoList.length){for(let i=0;i<nodeInfoList.length;i++){let info=nodeInfoList[i];let node=nodeList[i];let bindings=info.bindings;if(bindings){for(let i=0;i<bindings.length;i++){let binding=bindings[i];setupCompoundStorage(node,binding);addNotifyListener(node,inst,binding)}}node.__dataHost=inst}}}function setupCompoundStorage(node,binding){if(binding.isCompound){let storage=node.__dataCompoundStorage||(node.__dataCompoundStorage={});let parts=binding.parts;let literals=new Array(parts.length);for(let j=0;j<parts.length;j++){literals[j]=parts[j].literal}let target=binding.target;storage[target]=literals;if(binding.literal&&binding.kind=="property"){node[target]=binding.literal}}}function addNotifyListener(node,inst,binding){if(binding.listenerEvent){let part=binding.parts[0];node.addEventListener(binding.listenerEvent,function(e){handleNotification(e,inst,binding.target,part.source,part.negate)})}}function createMethodEffect(model,sig,type,effectFn,methodInfo,dynamicFn){dynamicFn=sig.static||dynamicFn&&(typeof dynamicFn!=="object"||dynamicFn[sig.methodName]);let info={methodName:sig.methodName,args:sig.args,methodInfo:methodInfo,dynamicFn:dynamicFn};for(let i=0,arg;i<sig.args.length&&(arg=sig.args[i]);i++){if(!arg.literal){model._addPropertyEffect(arg.rootProperty,type,{fn:effectFn,info:info,trigger:arg})}}if(dynamicFn){model._addPropertyEffect(sig.methodName,type,{fn:effectFn,info:info})}}function runMethodEffect(inst,property,props,oldProps,info){let context=inst._methodHost||inst;let fn=context[info.methodName];if(fn){let args=inst._marshalArgs(info.args,property,props);return fn.apply(context,args)}else if(!info.dynamicFn){console.warn("method `"+info.methodName+"` not defined")}}const emptyArray=[];const IDENT="(?:"+"[a-zA-Z_$][\\w.:$\\-*]*"+")";const NUMBER="(?:"+"[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?"+")";const SQUOTE_STRING="(?:"+"'(?:[^'\\\\]|\\\\.)*'"+")";const DQUOTE_STRING="(?:"+'"(?:[^"\\\\]|\\\\.)*"'+")";const STRING="(?:"+SQUOTE_STRING+"|"+DQUOTE_STRING+")";const ARGUMENT="(?:("+IDENT+"|"+NUMBER+"|"+STRING+")\\s*"+")";const ARGUMENTS="(?:"+ARGUMENT+"(?:,\\s*"+ARGUMENT+")*"+")";const ARGUMENT_LIST="(?:"+"\\(\\s*"+"(?:"+ARGUMENTS+"?"+")"+"\\)\\s*"+")";const BINDING="("+IDENT+"\\s*"+ARGUMENT_LIST+"?"+")";const OPEN_BRACKET="(\\[\\[|{{)"+"\\s*";const CLOSE_BRACKET="(?:]]|}})";const NEGATE="(?:(!)\\s*)?";const EXPRESSION=OPEN_BRACKET+NEGATE+BINDING+CLOSE_BRACKET;const bindingRegex=new RegExp(EXPRESSION,"g");function literalFromParts(parts){let s="";for(let i=0;i<parts.length;i++){let literal=parts[i].literal;s+=literal||""}return s}function parseMethod(expression){let m=expression.match(/([^\s]+?)\(([\s\S]*)\)/);if(m){let methodName=m[1];let sig={methodName:methodName,static:true,args:emptyArray};if(m[2].trim()){let args=m[2].replace(/\\,/g,"&comma;").split(",");return parseArgs(args,sig)}else{return sig}}return null}function parseArgs(argList,sig){sig.args=argList.map(function(rawArg){let arg=parseArg(rawArg);if(!arg.literal){sig.static=false}return arg},this);return sig}function parseArg(rawArg){let arg=rawArg.trim().replace(/&comma;/g,",").replace(/\\(.)/g,"$1");let a={name:arg,value:"",literal:false};let fc=arg[0];if(fc==="-"){fc=arg[1]}if(fc>="0"&&fc<="9"){fc="#"}switch(fc){case"'":case'"':a.value=arg.slice(1,-1);a.literal=true;break;case"#":a.value=Number(arg);a.literal=true;break}if(!a.literal){a.rootProperty=root(arg);a.structured=isPath(arg);if(a.structured){a.wildcard=arg.slice(-2)==".*";if(a.wildcard){a.name=arg.slice(0,-2)}}}return a}function getArgValue(data,props,path){let value=get(data,path);if(value===undefined){value=props[path]}return value}function notifySplices(inst,array,path,splices){inst.notifyPath(path+".splices",{indexSplices:splices});inst.notifyPath(path+".length",array.length)}function notifySplice(inst,array,path,index,addedCount,removed){notifySplices(inst,array,path,[{index:index,addedCount:addedCount,removed:removed,object:array,type:"splice"}])}function upper(name){return name[0].toUpperCase()+name.substring(1)}const PropertyEffects=dedupingMixin(superClass=>{const propertyEffectsBase=TemplateStamp(PropertyAccessors(superClass));class PropertyEffects extends propertyEffectsBase{constructor(){super();this.__isPropertyEffectsClient=true;this.__dataCounter=0;this.__dataClientsReady;this.__dataPendingClients;this.__dataToNotify;this.__dataLinkedPaths;this.__dataHasPaths;this.__dataCompoundStorage;this.__dataHost;this.__dataTemp;this.__dataClientsInitialized;this.__data;this.__dataPending;this.__dataOld;this.__computeEffects;this.__reflectEffects;this.__notifyEffects;this.__propagateEffects;this.__observeEffects;this.__readOnly;this.__templateInfo}get PROPERTY_EFFECT_TYPES(){return TYPES}_initializeProperties(){super._initializeProperties();hostStack.registerHost(this);this.__dataClientsReady=false;this.__dataPendingClients=null;this.__dataToNotify=null;this.__dataLinkedPaths=null;this.__dataHasPaths=false;this.__dataCompoundStorage=this.__dataCompoundStorage||null;this.__dataHost=this.__dataHost||null;this.__dataTemp={};this.__dataClientsInitialized=false}_initializeProtoProperties(props){this.__data=Object.create(props);this.__dataPending=Object.create(props);this.__dataOld={}}_initializeInstanceProperties(props){let readOnly=this[TYPES.READ_ONLY];for(let prop in props){if(!readOnly||!readOnly[prop]){this.__dataPending=this.__dataPending||{};this.__dataOld=this.__dataOld||{};this.__data[prop]=this.__dataPending[prop]=props[prop]}}}_addPropertyEffect(property,type,effect){this._createPropertyAccessor(property,type==TYPES.READ_ONLY);let effects=ensureOwnEffectMap(this,type)[property];if(!effects){effects=this[type][property]=[]}effects.push(effect)}_removePropertyEffect(property,type,effect){let effects=ensureOwnEffectMap(this,type)[property];let idx=effects.indexOf(effect);if(idx>=0){effects.splice(idx,1)}}_hasPropertyEffect(property,type){let effects=this[type];return Boolean(effects&&effects[property])}_hasReadOnlyEffect(property){return this._hasPropertyEffect(property,TYPES.READ_ONLY)}_hasNotifyEffect(property){return this._hasPropertyEffect(property,TYPES.NOTIFY)}_hasReflectEffect(property){return this._hasPropertyEffect(property,TYPES.REFLECT)}_hasComputedEffect(property){return this._hasPropertyEffect(property,TYPES.COMPUTE)}_setPendingPropertyOrPath(path,value,shouldNotify,isPathNotification){if(isPathNotification||root(Array.isArray(path)?path[0]:path)!==path){if(!isPathNotification){let old=get(this,path);path=set(this,path,value);if(!path||!super._shouldPropertyChange(path,value,old)){return false}}this.__dataHasPaths=true;if(this._setPendingProperty(path,value,shouldNotify)){computeLinkedPaths(this,path,value);return true}}else{if(this.__dataHasAccessor&&this.__dataHasAccessor[path]){return this._setPendingProperty(path,value,shouldNotify)}else{this[path]=value}}return false}_setUnmanagedPropertyToNode(node,prop,value){if(value!==node[prop]||typeof value=="object"){node[prop]=value}}_setPendingProperty(property,value,shouldNotify){let propIsPath=this.__dataHasPaths&&isPath(property);let prevProps=propIsPath?this.__dataTemp:this.__data;if(this._shouldPropertyChange(property,value,prevProps[property])){if(!this.__dataPending){this.__dataPending={};this.__dataOld={}}if(!(property in this.__dataOld)){this.__dataOld[property]=this.__data[property]}if(propIsPath){this.__dataTemp[property]=value}else{this.__data[property]=value}this.__dataPending[property]=value;if(propIsPath||this[TYPES.NOTIFY]&&this[TYPES.NOTIFY][property]){this.__dataToNotify=this.__dataToNotify||{};this.__dataToNotify[property]=shouldNotify}return true}return false}_setProperty(property,value){if(this._setPendingProperty(property,value,true)){this._invalidateProperties()}}_invalidateProperties(){if(this.__dataReady){this._flushProperties()}}_enqueueClient(client){this.__dataPendingClients=this.__dataPendingClients||[];if(client!==this){this.__dataPendingClients.push(client)}}_flushProperties(){this.__dataCounter++;super._flushProperties();this.__dataCounter--}_flushClients(){if(!this.__dataClientsReady){this.__dataClientsReady=true;this._readyClients();this.__dataReady=true}else{this.__enableOrFlushClients()}}__enableOrFlushClients(){let clients=this.__dataPendingClients;if(clients){this.__dataPendingClients=null;for(let i=0;i<clients.length;i++){let client=clients[i];if(!client.__dataEnabled){client._enableProperties()}else if(client.__dataPending){client._flushProperties()}}}}_readyClients(){this.__enableOrFlushClients()}setProperties(props,setReadOnly){for(let path in props){if(setReadOnly||!this[TYPES.READ_ONLY]||!this[TYPES.READ_ONLY][path]){this._setPendingPropertyOrPath(path,props[path],true)}}this._invalidateProperties()}ready(){this._flushProperties();if(!this.__dataClientsReady){this._flushClients()}if(this.__dataPending){this._flushProperties()}}_propertiesChanged(currentProps,changedProps,oldProps){let hasPaths=this.__dataHasPaths;this.__dataHasPaths=false;runComputedEffects(this,changedProps,oldProps,hasPaths);let notifyProps=this.__dataToNotify;this.__dataToNotify=null;this._propagatePropertyChanges(changedProps,oldProps,hasPaths);this._flushClients();runEffects(this,this[TYPES.REFLECT],changedProps,oldProps,hasPaths);runEffects(this,this[TYPES.OBSERVE],changedProps,oldProps,hasPaths);if(notifyProps){runNotifyEffects(this,notifyProps,changedProps,oldProps,hasPaths)}if(this.__dataCounter==1){this.__dataTemp={}}}_propagatePropertyChanges(changedProps,oldProps,hasPaths){if(this[TYPES.PROPAGATE]){runEffects(this,this[TYPES.PROPAGATE],changedProps,oldProps,hasPaths)}let templateInfo=this.__templateInfo;while(templateInfo){runEffects(this,templateInfo.propertyEffects,changedProps,oldProps,hasPaths,templateInfo.nodeList);templateInfo=templateInfo.nextTemplateInfo}}linkPaths(to,from){to=normalize(to);from=normalize(from);this.__dataLinkedPaths=this.__dataLinkedPaths||{};this.__dataLinkedPaths[to]=from}unlinkPaths(path){path=normalize(path);if(this.__dataLinkedPaths){delete this.__dataLinkedPaths[path]}}notifySplices(path,splices){let info={path:""};let array=get(this,path,info);notifySplices(this,array,info.path,splices)}get(path,root){return get(root||this,path)}set(path,value,root){if(root){set(root,path,value)}else{if(!this[TYPES.READ_ONLY]||!this[TYPES.READ_ONLY][path]){if(this._setPendingPropertyOrPath(path,value,true)){this._invalidateProperties()}}}}push(path,...items){let info={path:""};let array=get(this,path,info);let len=array.length;let ret=array.push(...items);if(items.length){notifySplice(this,array,info.path,len,items.length,[])}return ret}pop(path){let info={path:""};let array=get(this,path,info);let hadLength=Boolean(array.length);let ret=array.pop();if(hadLength){notifySplice(this,array,info.path,array.length,0,[ret])}return ret}splice(path,start,deleteCount,...items){let info={path:""};let array=get(this,path,info);if(start<0){start=array.length-Math.floor(-start)}else if(start){start=Math.floor(start)}let ret;if(arguments.length===2){ret=array.splice(start)}else{ret=array.splice(start,deleteCount,...items)}if(items.length||ret.length){notifySplice(this,array,info.path,start,items.length,ret)}return ret}shift(path){let info={path:""};let array=get(this,path,info);let hadLength=Boolean(array.length);let ret=array.shift();if(hadLength){notifySplice(this,array,info.path,0,0,[ret])}return ret}unshift(path,...items){let info={path:""};let array=get(this,path,info);let ret=array.unshift(...items);if(items.length){notifySplice(this,array,info.path,0,items.length,[])}return ret}notifyPath(path,value){let propPath;if(arguments.length==1){let info={path:""};value=get(this,path,info);propPath=info.path}else if(Array.isArray(path)){propPath=normalize(path)}else{propPath=path}if(this._setPendingPropertyOrPath(propPath,value,true,true)){this._invalidateProperties()}}_createReadOnlyProperty(property,protectedSetter){this._addPropertyEffect(property,TYPES.READ_ONLY);if(protectedSetter){this["_set"+upper(property)]=function(value){this._setProperty(property,value)}}}_createPropertyObserver(property,method,dynamicFn){let info={property:property,method:method,dynamicFn:Boolean(dynamicFn)};this._addPropertyEffect(property,TYPES.OBSERVE,{fn:runObserverEffect,info:info,trigger:{name:property}});if(dynamicFn){this._addPropertyEffect(method,TYPES.OBSERVE,{fn:runObserverEffect,info:info,trigger:{name:method}})}}_createMethodObserver(expression,dynamicFn){let sig=parseMethod(expression);if(!sig){throw new Error("Malformed observer expression '"+expression+"'")}createMethodEffect(this,sig,TYPES.OBSERVE,runMethodEffect,null,dynamicFn)}_createNotifyingProperty(property){this._addPropertyEffect(property,TYPES.NOTIFY,{fn:runNotifyEffect,info:{eventName:camelToDashCase(property)+"-changed",property:property}})}_createReflectedProperty(property){let attr=this.constructor.attributeNameForProperty(property);if(attr[0]==="-"){console.warn("Property "+property+" cannot be reflected to attribute "+attr+' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.')}else{this._addPropertyEffect(property,TYPES.REFLECT,{fn:runReflectEffect,info:{attrName:attr}})}}_createComputedProperty(property,expression,dynamicFn){let sig=parseMethod(expression);if(!sig){throw new Error("Malformed computed expression '"+expression+"'")}createMethodEffect(this,sig,TYPES.COMPUTE,runComputedEffect,property,dynamicFn)}_marshalArgs(args,path,props){const data=this.__data;const values=[];for(let i=0,l=args.length;i<l;i++){let{name:name,structured:structured,wildcard:wildcard,value:value,literal:literal}=args[i];if(!literal){if(wildcard){const matches=isDescendant(name,path);const pathValue=getArgValue(data,props,matches?path:name);value={path:matches?path:name,value:pathValue,base:matches?get(data,name):pathValue}}else{value=structured?getArgValue(data,props,name):data[name]}}values[i]=value}return values}static addPropertyEffect(property,type,effect){this.prototype._addPropertyEffect(property,type,effect)}static createPropertyObserver(property,method,dynamicFn){this.prototype._createPropertyObserver(property,method,dynamicFn)}static createMethodObserver(expression,dynamicFn){this.prototype._createMethodObserver(expression,dynamicFn)}static createNotifyingProperty(property){this.prototype._createNotifyingProperty(property)}static createReadOnlyProperty(property,protectedSetter){this.prototype._createReadOnlyProperty(property,protectedSetter)}static createReflectedProperty(property){this.prototype._createReflectedProperty(property)}static createComputedProperty(property,expression,dynamicFn){this.prototype._createComputedProperty(property,expression,dynamicFn)}static bindTemplate(template){return this.prototype._bindTemplate(template)}_bindTemplate(template,instanceBinding){let templateInfo=this.constructor._parseTemplate(template);let wasPreBound=this.__templateInfo==templateInfo;if(!wasPreBound){for(let prop in templateInfo.propertyEffects){this._createPropertyAccessor(prop)}}if(instanceBinding){templateInfo=Object.create(templateInfo);templateInfo.wasPreBound=wasPreBound;if(!wasPreBound&&this.__templateInfo){let last=this.__templateInfoLast||this.__templateInfo;this.__templateInfoLast=last.nextTemplateInfo=templateInfo;templateInfo.previousTemplateInfo=last;return templateInfo}}return this.__templateInfo=templateInfo}static _addTemplatePropertyEffect(templateInfo,prop,effect){let hostProps=templateInfo.hostProps=templateInfo.hostProps||{};hostProps[prop]=true;let effects=templateInfo.propertyEffects=templateInfo.propertyEffects||{};let propEffects=effects[prop]=effects[prop]||[];propEffects.push(effect)}_stampTemplate(template){hostStack.beginHosting(this);let dom=super._stampTemplate(template);hostStack.endHosting(this);let templateInfo=this._bindTemplate(template,true);templateInfo.nodeList=dom.nodeList;if(!templateInfo.wasPreBound){let nodes=templateInfo.childNodes=[];for(let n=dom.firstChild;n;n=n.nextSibling){nodes.push(n)}}dom.templateInfo=templateInfo;setupBindings(this,templateInfo);if(this.__dataReady){runEffects(this,templateInfo.propertyEffects,this.__data,null,false,templateInfo.nodeList)}return dom}_removeBoundDom(dom){let templateInfo=dom.templateInfo;if(templateInfo.previousTemplateInfo){templateInfo.previousTemplateInfo.nextTemplateInfo=templateInfo.nextTemplateInfo}if(templateInfo.nextTemplateInfo){templateInfo.nextTemplateInfo.previousTemplateInfo=templateInfo.previousTemplateInfo}if(this.__templateInfoLast==templateInfo){this.__templateInfoLast=templateInfo.previousTemplateInfo}templateInfo.previousTemplateInfo=templateInfo.nextTemplateInfo=null;let nodes=templateInfo.childNodes;for(let i=0;i<nodes.length;i++){let node=nodes[i];node.parentNode.removeChild(node)}}static _parseTemplateNode(node,templateInfo,nodeInfo){let noted=super._parseTemplateNode(node,templateInfo,nodeInfo);if(node.nodeType===Node.TEXT_NODE){let parts=this._parseBindings(node.textContent,templateInfo);if(parts){node.textContent=literalFromParts(parts)||" ";addBinding(this,templateInfo,nodeInfo,"text","textContent",parts);noted=true}}return noted}static _parseTemplateNodeAttribute(node,templateInfo,nodeInfo,name,value){let parts=this._parseBindings(value,templateInfo);if(parts){let origName=name;let kind="property";if(capitalAttributeRegex.test(name)){kind="attribute"}else if(name[name.length-1]=="$"){name=name.slice(0,-1);kind="attribute"}let literal=literalFromParts(parts);if(literal&&kind=="attribute"){if(name=="class"&&node.hasAttribute("class")){literal+=" "+node.getAttribute(name)}node.setAttribute(name,literal)}if(node.localName==="input"&&origName==="value"){node.setAttribute(origName,"")}node.removeAttribute(origName);if(kind==="property"){name=dashToCamelCase(name)}addBinding(this,templateInfo,nodeInfo,kind,name,parts,literal);return true}else{return super._parseTemplateNodeAttribute(node,templateInfo,nodeInfo,name,value)}}static _parseTemplateNestedTemplate(node,templateInfo,nodeInfo){let noted=super._parseTemplateNestedTemplate(node,templateInfo,nodeInfo);let hostProps=nodeInfo.templateInfo.hostProps;let mode="{";for(let source in hostProps){let parts=[{mode:mode,source:source,dependencies:[source]}];addBinding(this,templateInfo,nodeInfo,"property","_host_"+source,parts)}return noted}static _parseBindings(text,templateInfo){let parts=[];let lastIndex=0;let m;while((m=bindingRegex.exec(text))!==null){if(m.index>lastIndex){parts.push({literal:text.slice(lastIndex,m.index)})}let mode=m[1][0];let negate=Boolean(m[2]);let source=m[3].trim();let customEvent=false,notifyEvent="",colon=-1;if(mode=="{"&&(colon=source.indexOf("::"))>0){notifyEvent=source.substring(colon+2);source=source.substring(0,colon);customEvent=true}let signature=parseMethod(source);let dependencies=[];if(signature){let{args:args,methodName:methodName}=signature;for(let i=0;i<args.length;i++){let arg=args[i];if(!arg.literal){dependencies.push(arg)}}let dynamicFns=templateInfo.dynamicFns;if(dynamicFns&&dynamicFns[methodName]||signature.static){dependencies.push(methodName);signature.dynamicFn=true}}else{dependencies.push(source)}parts.push({source:source,mode:mode,negate:negate,customEvent:customEvent,signature:signature,dependencies:dependencies,event:notifyEvent});lastIndex=bindingRegex.lastIndex}if(lastIndex&&lastIndex<text.length){let literal=text.substring(lastIndex);if(literal){parts.push({literal:literal})}}if(parts.length){return parts}else{return null}}static _evaluateBinding(inst,part,path,props,oldProps,hasPaths){let value;if(part.signature){value=runMethodEffect(inst,path,props,oldProps,part.signature)}else if(path!=part.source){value=get(inst,part.source)}else{if(hasPaths&&isPath(path)){value=get(inst,path)}else{value=inst.__data[path]}}if(part.negate){value=!value}return value}}return PropertyEffects});class HostStack{constructor(){this.stack=[]}registerHost(inst){if(this.stack.length){let host=this.stack[this.stack.length-1];host._enqueueClient(inst)}}beginHosting(inst){this.stack.push(inst)}endHosting(inst){let stackLen=this.stack.length;if(stackLen&&this.stack[stackLen-1]==inst){this.stack.pop()}}}const hostStack=new HostStack;function register$1(prototype){}function normalizeProperties(props){const output={};for(let p in props){const o=props[p];output[p]=typeof o==="function"?{type:o}:o}return output}const PropertiesMixin=dedupingMixin(superClass=>{const base=PropertiesChanged(superClass);function superPropertiesClass(constructor){const superCtor=Object.getPrototypeOf(constructor);return superCtor.prototype instanceof PropertiesMixin?superCtor:null}function ownProperties(constructor){if(!constructor.hasOwnProperty(JSCompiler_renameProperty("__ownProperties",constructor))){let props=null;if(constructor.hasOwnProperty(JSCompiler_renameProperty("properties",constructor))){const properties=constructor.properties;if(properties){props=normalizeProperties(properties)}}constructor.__ownProperties=props}return constructor.__ownProperties}class PropertiesMixin extends base{static get observedAttributes(){if(!this.hasOwnProperty("__observedAttributes")){register$1(this.prototype);const props=this._properties;this.__observedAttributes=props?Object.keys(props).map(p=>this.attributeNameForProperty(p)):[]}return this.__observedAttributes}static finalize(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__finalized",this))){const superCtor=superPropertiesClass(this);if(superCtor){superCtor.finalize()}this.__finalized=true;this._finalizeClass()}}static _finalizeClass(){const props=ownProperties(this);if(props){this.createProperties(props)}}static get _properties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__properties",this))){const superCtor=superPropertiesClass(this);this.__properties=Object.assign({},superCtor&&superCtor._properties,ownProperties(this))}return this.__properties}static typeForProperty(name){const info=this._properties[name];return info&&info.type}_initializeProperties(){this.constructor.finalize();super._initializeProperties()}connectedCallback(){if(super.connectedCallback){super.connectedCallback()}this._enableProperties()}disconnectedCallback(){if(super.disconnectedCallback){super.disconnectedCallback()}}}return PropertiesMixin});const version="3.2.0";const builtCSS=window.ShadyCSS&&window.ShadyCSS["cssBuild"];const ElementMixin=dedupingMixin(base=>{const polymerElementBase=PropertiesMixin(PropertyEffects(base));function propertyDefaults(constructor){if(!constructor.hasOwnProperty(JSCompiler_renameProperty("__propertyDefaults",constructor))){constructor.__propertyDefaults=null;let props=constructor._properties;for(let p in props){let info=props[p];if("value"in info){constructor.__propertyDefaults=constructor.__propertyDefaults||{};constructor.__propertyDefaults[p]=info}}}return constructor.__propertyDefaults}function ownObservers(constructor){if(!constructor.hasOwnProperty(JSCompiler_renameProperty("__ownObservers",constructor))){constructor.__ownObservers=constructor.hasOwnProperty(JSCompiler_renameProperty("observers",constructor))?constructor.observers:null}return constructor.__ownObservers}function createPropertyFromConfig(proto,name,info,allProps){if(info.computed){info.readOnly=true}if(info.computed){if(proto._hasReadOnlyEffect(name)){console.warn(`Cannot redefine computed property '${name}'.`)}else{proto._createComputedProperty(name,info.computed,allProps)}}if(info.readOnly&&!proto._hasReadOnlyEffect(name)){proto._createReadOnlyProperty(name,!info.computed)}else if(info.readOnly===false&&proto._hasReadOnlyEffect(name)){console.warn(`Cannot make readOnly property '${name}' non-readOnly.`)}if(info.reflectToAttribute&&!proto._hasReflectEffect(name)){proto._createReflectedProperty(name)}else if(info.reflectToAttribute===false&&proto._hasReflectEffect(name)){console.warn(`Cannot make reflected property '${name}' non-reflected.`)}if(info.notify&&!proto._hasNotifyEffect(name)){proto._createNotifyingProperty(name)}else if(info.notify===false&&proto._hasNotifyEffect(name)){console.warn(`Cannot make notify property '${name}' non-notify.`)}if(info.observer){proto._createPropertyObserver(name,info.observer,allProps[info.observer])}proto._addPropertyToAttributeMap(name)}function processElementStyles(klass,template,is,baseURI){if(!builtCSS){const templateStyles=template.content.querySelectorAll("style");const stylesWithImports=stylesFromTemplate(template);const linkedStyles=stylesFromModuleImports(is);const firstTemplateChild=template.content.firstElementChild;for(let idx=0;idx<linkedStyles.length;idx++){let s=linkedStyles[idx];s.textContent=klass._processStyleText(s.textContent,baseURI);template.content.insertBefore(s,firstTemplateChild)}let templateStyleIndex=0;for(let i=0;i<stylesWithImports.length;i++){let s=stylesWithImports[i];let templateStyle=templateStyles[templateStyleIndex];if(templateStyle!==s){s=s.cloneNode(true);templateStyle.parentNode.insertBefore(s,templateStyle)}else{templateStyleIndex++}s.textContent=klass._processStyleText(s.textContent,baseURI)}}if(window.ShadyCSS){window.ShadyCSS.prepareTemplate(template,is)}}function getTemplateFromDomModule(is){let template=null;if(is&&(!strictTemplatePolicy||allowTemplateFromDomModule)){template=DomModule.import(is,"template");if(strictTemplatePolicy&&!template){throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${is}`)}}return template}class PolymerElement extends polymerElementBase{static get polymerElementVersion(){return version}static _finalizeClass(){super._finalizeClass();const observers=ownObservers(this);if(observers){this.createObservers(observers,this._properties)}this._prepareTemplate()}static _prepareTemplate(){let template=this.template;if(template){if(typeof template==="string"){console.error("template getter must return HTMLTemplateElement");template=null}else if(!legacyOptimizations){template=template.cloneNode(true)}}this.prototype._template=template}static createProperties(props){for(let p in props){createPropertyFromConfig(this.prototype,p,props[p],props)}}static createObservers(observers,dynamicFns){const proto=this.prototype;for(let i=0;i<observers.length;i++){proto._createMethodObserver(observers[i],dynamicFns)}}static get template(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_template",this))){this._template=this.prototype.hasOwnProperty(JSCompiler_renameProperty("_template",this.prototype))?this.prototype._template:getTemplateFromDomModule(this.is)||Object.getPrototypeOf(this.prototype).constructor.template}return this._template}static set template(value){this._template=value}static get importPath(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_importPath",this))){const meta=this.importMeta;if(meta){this._importPath=pathFromUrl(meta.url)}else{const module=DomModule.import(this.is);this._importPath=module&&module.assetpath||Object.getPrototypeOf(this.prototype).constructor.importPath}}return this._importPath}constructor(){super();this._template;this._importPath;this.rootPath;this.importPath;this.root;this.$}_initializeProperties(){this.constructor.finalize();this.constructor._finalizeTemplate(this.localName);super._initializeProperties();this.rootPath=rootPath;this.importPath=this.constructor.importPath;let p$=propertyDefaults(this.constructor);if(!p$){return}for(let p in p$){let info=p$[p];if(!this.hasOwnProperty(p)){let value=typeof info.value=="function"?info.value.call(this):info.value;if(this._hasAccessor(p)){this._setPendingProperty(p,value,true)}else{this[p]=value}}}}static _processStyleText(cssText,baseURI){return resolveCss(cssText,baseURI)}static _finalizeTemplate(is){const template=this.prototype._template;if(template&&!template.__polymerFinalized){template.__polymerFinalized=true;const importPath=this.importPath;const baseURI=importPath?resolveUrl(importPath):"";processElementStyles(this,template,is,baseURI);this.prototype._bindTemplate(template)}}connectedCallback(){if(window.ShadyCSS&&this._template){window.ShadyCSS.styleElement(this)}super.connectedCallback()}ready(){if(this._template){this.root=this._stampTemplate(this._template);this.$=this.root.$}super.ready()}_readyClients(){if(this._template){this.root=this._attachDom(this.root)}super._readyClients()}_attachDom(dom){const n=wrap(this);if(n.attachShadow){if(dom){if(!n.shadowRoot){n.attachShadow({mode:"open"})}n.shadowRoot.appendChild(dom);if(syncInitialRender&&window.ShadyDOM){ShadyDOM.flushInitial(n.shadowRoot)}return n.shadowRoot}return null}else{throw new Error("ShadowDOM not available. "+"PolymerElement can create dom as children instead of in "+"ShadowDOM by setting `this.root = this;` before `ready`.")}}updateStyles(properties){if(window.ShadyCSS){window.ShadyCSS.styleSubtree(this,properties)}}resolveUrl(url,base){if(!base&&this.importPath){base=resolveUrl(this.importPath)}return resolveUrl(url,base)}static _parseTemplateContent(template,templateInfo,nodeInfo){templateInfo.dynamicFns=templateInfo.dynamicFns||this._properties;return super._parseTemplateContent(template,templateInfo,nodeInfo)}static _addTemplatePropertyEffect(templateInfo,prop,effect){if(legacyOptimizations&&!(prop in this._properties)){console.warn(`Property '${prop}' used in template but not declared in 'properties'; `+`attribute will not be observed.`)}return super._addTemplatePropertyEffect(templateInfo,prop,effect)}}return PolymerElement});const GestureEventListeners=dedupingMixin(superClass=>{class GestureEventListeners extends superClass{_addEventListenerToNode(node,eventName,handler){if(!addListener(node,eventName,handler)){super._addEventListenerToNode(node,eventName,handler)}}_removeEventListenerFromNode(node,eventName,handler){if(!removeListener(node,eventName,handler)){super._removeEventListenerFromNode(node,eventName,handler)}}}return GestureEventListeners});function resolve(){document.body.removeAttribute("unresolved")}if(document.readyState==="interactive"||document.readyState==="complete"){resolve()}else{window.addEventListener("DOMContentLoaded",resolve)}function newSplice(index,removed,addedCount){return{index:index,removed:removed,addedCount:addedCount}}const EDIT_LEAVE=0;const EDIT_UPDATE=1;const EDIT_ADD=2;const EDIT_DELETE=3;function calcEditDistances(current,currentStart,currentEnd,old,oldStart,oldEnd){let rowCount=oldEnd-oldStart+1;let columnCount=currentEnd-currentStart+1;let distances=new Array(rowCount);for(let i=0;i<rowCount;i++){distances[i]=new Array(columnCount);distances[i][0]=i}for(let j=0;j<columnCount;j++)distances[0][j]=j;for(let i=1;i<rowCount;i++){for(let j=1;j<columnCount;j++){if(equals(current[currentStart+j-1],old[oldStart+i-1]))distances[i][j]=distances[i-1][j-1];else{let north=distances[i-1][j]+1;let west=distances[i][j-1]+1;distances[i][j]=north<west?north:west}}}return distances}function spliceOperationsFromEditDistances(distances){let i=distances.length-1;let j=distances[0].length-1;let current=distances[i][j];let edits=[];while(i>0||j>0){if(i==0){edits.push(EDIT_ADD);j--;continue}if(j==0){edits.push(EDIT_DELETE);i--;continue}let northWest=distances[i-1][j-1];let west=distances[i-1][j];let north=distances[i][j-1];let min;if(west<north)min=west<northWest?west:northWest;else min=north<northWest?north:northWest;if(min==northWest){if(northWest==current){edits.push(EDIT_LEAVE)}else{edits.push(EDIT_UPDATE);current=northWest}i--;j--}else if(min==west){edits.push(EDIT_DELETE);i--;current=west}else{edits.push(EDIT_ADD);j--;current=north}}edits.reverse();return edits}function calcSplices(current,currentStart,currentEnd,old,oldStart,oldEnd){let prefixCount=0;let suffixCount=0;let splice;let minLength=Math.min(currentEnd-currentStart,oldEnd-oldStart);if(currentStart==0&&oldStart==0)prefixCount=sharedPrefix(current,old,minLength);if(currentEnd==current.length&&oldEnd==old.length)suffixCount=sharedSuffix(current,old,minLength-prefixCount);currentStart+=prefixCount;oldStart+=prefixCount;currentEnd-=suffixCount;oldEnd-=suffixCount;if(currentEnd-currentStart==0&&oldEnd-oldStart==0)return[];if(currentStart==currentEnd){splice=newSplice(currentStart,[],0);while(oldStart<oldEnd)splice.removed.push(old[oldStart++]);return[splice]}else if(oldStart==oldEnd)return[newSplice(currentStart,[],currentEnd-currentStart)];let ops=spliceOperationsFromEditDistances(calcEditDistances(current,currentStart,currentEnd,old,oldStart,oldEnd));splice=undefined;let splices=[];let index=currentStart;let oldIndex=oldStart;for(let i=0;i<ops.length;i++){switch(ops[i]){case EDIT_LEAVE:if(splice){splices.push(splice);splice=undefined}index++;oldIndex++;break;case EDIT_UPDATE:if(!splice)splice=newSplice(index,[],0);splice.addedCount++;index++;splice.removed.push(old[oldIndex]);oldIndex++;break;case EDIT_ADD:if(!splice)splice=newSplice(index,[],0);splice.addedCount++;index++;break;case EDIT_DELETE:if(!splice)splice=newSplice(index,[],0);splice.removed.push(old[oldIndex]);oldIndex++;break}}if(splice){splices.push(splice)}return splices}function sharedPrefix(current,old,searchLength){for(let i=0;i<searchLength;i++)if(!equals(current[i],old[i]))return i;return searchLength}function sharedSuffix(current,old,searchLength){let index1=current.length;let index2=old.length;let count=0;while(count<searchLength&&equals(current[--index1],old[--index2]))count++;return count}function calculateSplices(current,previous){return calcSplices(current,0,current.length,previous,0,previous.length)}function equals(currentValue,previousValue){return currentValue===previousValue}function isSlot(node){return node.localName==="slot"}let FlattenedNodesObserver=class{static getFlattenedNodes(node){const wrapped=wrap(node);if(isSlot(node)){node=node;return wrapped.assignedNodes({flatten:true})}else{return Array.from(wrapped.childNodes).map(node=>{if(isSlot(node)){node=node;return wrap(node).assignedNodes({flatten:true})}else{return[node]}}).reduce((a,b)=>a.concat(b),[])}}constructor(target,callback){this._shadyChildrenObserver=null;this._nativeChildrenObserver=null;this._connected=false;this._target=target;this.callback=callback;this._effectiveNodes=[];this._observer=null;this._scheduled=false;this._boundSchedule=(()=>{this._schedule()});this.connect();this._schedule()}connect(){if(isSlot(this._target)){this._listenSlots([this._target])}else if(wrap(this._target).children){this._listenSlots(wrap(this._target).children);if(window.ShadyDOM){this._shadyChildrenObserver=ShadyDOM.observeChildren(this._target,mutations=>{this._processMutations(mutations)})}else{this._nativeChildrenObserver=new MutationObserver(mutations=>{this._processMutations(mutations)});this._nativeChildrenObserver.observe(this._target,{childList:true})}}this._connected=true}disconnect(){if(isSlot(this._target)){this._unlistenSlots([this._target])}else if(wrap(this._target).children){this._unlistenSlots(wrap(this._target).children);if(window.ShadyDOM&&this._shadyChildrenObserver){ShadyDOM.unobserveChildren(this._shadyChildrenObserver);this._shadyChildrenObserver=null}else if(this._nativeChildrenObserver){this._nativeChildrenObserver.disconnect();this._nativeChildrenObserver=null}}this._connected=false}_schedule(){if(!this._scheduled){this._scheduled=true;microTask.run(()=>this.flush())}}_processMutations(mutations){this._processSlotMutations(mutations);this.flush()}_processSlotMutations(mutations){if(mutations){for(let i=0;i<mutations.length;i++){let mutation=mutations[i];if(mutation.addedNodes){this._listenSlots(mutation.addedNodes)}if(mutation.removedNodes){this._unlistenSlots(mutation.removedNodes)}}}}flush(){if(!this._connected){return false}if(window.ShadyDOM){ShadyDOM.flush()}if(this._nativeChildrenObserver){this._processSlotMutations(this._nativeChildrenObserver.takeRecords())}else if(this._shadyChildrenObserver){this._processSlotMutations(this._shadyChildrenObserver.takeRecords())}this._scheduled=false;let info={target:this._target,addedNodes:[],removedNodes:[]};let newNodes=this.constructor.getFlattenedNodes(this._target);let splices=calculateSplices(newNodes,this._effectiveNodes);for(let i=0,s;i<splices.length&&(s=splices[i]);i++){for(let j=0,n;j<s.removed.length&&(n=s.removed[j]);j++){info.removedNodes.push(n)}}for(let i=0,s;i<splices.length&&(s=splices[i]);i++){for(let j=s.index;j<s.index+s.addedCount;j++){info.addedNodes.push(newNodes[j])}}this._effectiveNodes=newNodes;let didFlush=false;if(info.addedNodes.length||info.removedNodes.length){didFlush=true;this.callback.call(this._target,info)}return didFlush}_listenSlots(nodeList){for(let i=0;i<nodeList.length;i++){let n=nodeList[i];if(isSlot(n)){n.addEventListener("slotchange",this._boundSchedule)}}}_unlistenSlots(nodeList){for(let i=0;i<nodeList.length;i++){let n=nodeList[i];if(isSlot(n)){n.removeEventListener("slotchange",this._boundSchedule)}}}};const flush=function(){let shadyDOM,debouncers;do{shadyDOM=window.ShadyDOM&&ShadyDOM.flush();if(window.ShadyCSS&&window.ShadyCSS.ScopingShim){window.ShadyCSS.ScopingShim.flush()}debouncers=flushDebouncers()}while(shadyDOM||debouncers)};const p=Element.prototype;const normalizedMatchesSelector=p.matches||p.matchesSelector||p.mozMatchesSelector||p.msMatchesSelector||p.oMatchesSelector||p.webkitMatchesSelector;const matchesSelector=function(node,selector){return normalizedMatchesSelector.call(node,selector)};class DomApiNative{constructor(node){this.node=node}observeNodes(callback){return new FlattenedNodesObserver(this.node,callback)}unobserveNodes(observerHandle){observerHandle.disconnect()}notifyObserver(){}deepContains(node){if(wrap(this.node).contains(node)){return true}let n=node;let doc=node.ownerDocument;while(n&&n!==doc&&n!==this.node){n=wrap(n).parentNode||wrap(n).host}return n===this.node}getOwnerRoot(){return wrap(this.node).getRootNode()}getDistributedNodes(){return this.node.localName==="slot"?wrap(this.node).assignedNodes({flatten:true}):[]}getDestinationInsertionPoints(){let ip$=[];let n=wrap(this.node).assignedSlot;while(n){ip$.push(n);n=wrap(n).assignedSlot}return ip$}importNode(node,deep){let doc=this.node instanceof Document?this.node:this.node.ownerDocument;return wrap(doc).importNode(node,deep)}getEffectiveChildNodes(){return FlattenedNodesObserver.getFlattenedNodes(this.node)}queryDistributedElements(selector){let c$=this.getEffectiveChildNodes();let list=[];for(let i=0,l=c$.length,c;i<l&&(c=c$[i]);i++){if(c.nodeType===Node.ELEMENT_NODE&&matchesSelector(c,selector)){list.push(c)}}return list}get activeElement(){let node=this.node;return node._activeElement!==undefined?node._activeElement:node.activeElement}}function forwardMethods(proto,methods){for(let i=0;i<methods.length;i++){let method=methods[i];proto[method]=function(){return this.node[method].apply(this.node,arguments)}}}function forwardReadOnlyProperties(proto,properties){for(let i=0;i<properties.length;i++){let name=properties[i];Object.defineProperty(proto,name,{get:function(){const domApi=this;return domApi.node[name]},configurable:true})}}function forwardProperties(proto,properties){for(let i=0;i<properties.length;i++){let name=properties[i];Object.defineProperty(proto,name,{get:function(){return this.node[name]},set:function(value){this.node[name]=value},configurable:true})}}class EventApi{constructor(event){this.event=event}get rootTarget(){return this.path[0]}get localTarget(){return this.event.target}get path(){return this.event.composedPath()}}DomApiNative.prototype.cloneNode;DomApiNative.prototype.appendChild;DomApiNative.prototype.insertBefore;DomApiNative.prototype.removeChild;DomApiNative.prototype.replaceChild;DomApiNative.prototype.setAttribute;DomApiNative.prototype.removeAttribute;DomApiNative.prototype.querySelector;DomApiNative.prototype.querySelectorAll;DomApiNative.prototype.parentNode;DomApiNative.prototype.firstChild;DomApiNative.prototype.lastChild;DomApiNative.prototype.nextSibling;DomApiNative.prototype.previousSibling;DomApiNative.prototype.firstElementChild;DomApiNative.prototype.lastElementChild;DomApiNative.prototype.nextElementSibling;DomApiNative.prototype.previousElementSibling;DomApiNative.prototype.childNodes;DomApiNative.prototype.children;DomApiNative.prototype.classList;DomApiNative.prototype.textContent;DomApiNative.prototype.innerHTML;let DomApiImpl=DomApiNative;if(window["ShadyDOM"]&&window["ShadyDOM"]["inUse"]&&window["ShadyDOM"]["noPatch"]&&window["ShadyDOM"]["Wrapper"]){class Wrapper extends window["ShadyDOM"]["Wrapper"]{}Object.getOwnPropertyNames(DomApiNative.prototype).forEach(prop=>{if(prop!="activeElement"){Wrapper.prototype[prop]=DomApiNative.prototype[prop]}});forwardReadOnlyProperties(Wrapper.prototype,["classList"]);DomApiImpl=Wrapper;Object.defineProperties(EventApi.prototype,{localTarget:{get(){return this.event.currentTarget},configurable:true},path:{get(){return window["ShadyDOM"]["composedPath"](this.event)},configurable:true}})}else{forwardMethods(DomApiNative.prototype,["cloneNode","appendChild","insertBefore","removeChild","replaceChild","setAttribute","removeAttribute","querySelector","querySelectorAll"]);forwardReadOnlyProperties(DomApiNative.prototype,["parentNode","firstChild","lastChild","nextSibling","previousSibling","firstElementChild","lastElementChild","nextElementSibling","previousElementSibling","childNodes","children","classList"]);forwardProperties(DomApiNative.prototype,["textContent","innerHTML"])}const dom=function(obj){obj=obj||document;if(obj instanceof DomApiImpl){return obj}if(obj instanceof EventApi){return obj}let helper=obj["__domApi"];if(!helper){if(obj instanceof Event){helper=new EventApi(obj)}else{helper=new DomApiImpl(obj)}obj["__domApi"]=helper}return helper};let styleInterface=window.ShadyCSS;const LegacyElementMixin=dedupingMixin(base=>{const legacyElementBase=GestureEventListeners(ElementMixin(base));const DIRECTION_MAP={x:"pan-x",y:"pan-y",none:"none",all:"auto"};class LegacyElement extends legacyElementBase{constructor(){super();this.isAttached;this.__boundListeners;this._debouncers}static get importMeta(){return this.prototype.importMeta}created(){}connectedCallback(){super.connectedCallback();this.isAttached=true;this.attached()}attached(){}disconnectedCallback(){super.disconnectedCallback();this.isAttached=false;this.detached()}detached(){}attributeChangedCallback(name,old,value,namespace){if(old!==value){super.attributeChangedCallback(name,old,value,namespace);this.attributeChanged(name,old,value)}}attributeChanged(name,old,value){}_initializeProperties(){let proto=Object.getPrototypeOf(this);if(!proto.hasOwnProperty("__hasRegisterFinished")){this._registered();proto.__hasRegisterFinished=true}super._initializeProperties();this.root=this;this.created();this._applyListeners()}_registered(){}ready(){this._ensureAttributes();super.ready()}_ensureAttributes(){}_applyListeners(){}serialize(value){return this._serializeValue(value)}deserialize(value,type){return this._deserializeValue(value,type)}reflectPropertyToAttribute(property,attribute,value){this._propertyToAttribute(property,attribute,value)}serializeValueToAttribute(value,attribute,node){this._valueToNodeAttribute(node||this,value,attribute)}extend(prototype,api){if(!(prototype&&api)){return prototype||api}let n$=Object.getOwnPropertyNames(api);for(let i=0,n;i<n$.length&&(n=n$[i]);i++){let pd=Object.getOwnPropertyDescriptor(api,n);if(pd){Object.defineProperty(prototype,n,pd)}}return prototype}mixin(target,source){for(let i in source){target[i]=source[i]}return target}chainObject(object,prototype){if(object&&prototype&&object!==prototype){object.__proto__=prototype}return object}instanceTemplate(template){let content=this.constructor._contentForTemplate(template);let dom=document.importNode(content,true);return dom}fire(type,detail,options){options=options||{};detail=detail===null||detail===undefined?{}:detail;let event=new Event(type,{bubbles:options.bubbles===undefined?true:options.bubbles,cancelable:Boolean(options.cancelable),composed:options.composed===undefined?true:options.composed});event.detail=detail;let node=options.node||this;wrap(node).dispatchEvent(event);return event}listen(node,eventName,methodName){node=node||this;let hbl=this.__boundListeners||(this.__boundListeners=new WeakMap);let bl=hbl.get(node);if(!bl){bl={};hbl.set(node,bl)}let key=eventName+methodName;if(!bl[key]){bl[key]=this._addMethodEventListenerToNode(node,eventName,methodName,this)}}unlisten(node,eventName,methodName){node=node||this;let bl=this.__boundListeners&&this.__boundListeners.get(node);let key=eventName+methodName;let handler=bl&&bl[key];if(handler){this._removeEventListenerFromNode(node,eventName,handler);bl[key]=null}}setScrollDirection(direction,node){setTouchAction(node||this,DIRECTION_MAP[direction]||"auto")}$$(slctr){return this.root.querySelector(slctr)}get domHost(){let root=wrap(this).getRootNode();return root instanceof DocumentFragment?root.host:root}distributeContent(){const thisEl=this;const domApi=dom(thisEl);if(window.ShadyDOM&&domApi.shadowRoot){ShadyDOM.flush()}}getEffectiveChildNodes(){const thisEl=this;const domApi=dom(thisEl);return domApi.getEffectiveChildNodes()}queryDistributedElements(selector){const thisEl=this;const domApi=dom(thisEl);return domApi.queryDistributedElements(selector)}getEffectiveChildren(){let list=this.getEffectiveChildNodes();return list.filter(function(n){return n.nodeType===Node.ELEMENT_NODE})}getEffectiveTextContent(){let cn=this.getEffectiveChildNodes();let tc=[];for(let i=0,c;c=cn[i];i++){if(c.nodeType!==Node.COMMENT_NODE){tc.push(c.textContent)}}return tc.join("")}queryEffectiveChildren(selector){let e$=this.queryDistributedElements(selector);return e$&&e$[0]}queryAllEffectiveChildren(selector){return this.queryDistributedElements(selector)}getContentChildNodes(slctr){let content=this.root.querySelector(slctr||"slot");return content?dom(content).getDistributedNodes():[]}getContentChildren(slctr){let children=this.getContentChildNodes(slctr).filter(function(n){return n.nodeType===Node.ELEMENT_NODE});return children}isLightDescendant(node){const thisNode=this;return thisNode!==node&&wrap(thisNode).contains(node)&&wrap(thisNode).getRootNode()===wrap(node).getRootNode()}isLocalDescendant(node){return this.root===wrap(node).getRootNode()}scopeSubtree(container,shouldObserve){}getComputedStyleValue(property){return styleInterface.getComputedStyleValue(this,property)}debounce(jobName,callback,wait){this._debouncers=this._debouncers||{};return this._debouncers[jobName]=Debouncer.debounce(this._debouncers[jobName],wait>0?timeOut.after(wait):microTask,callback.bind(this))}isDebouncerActive(jobName){this._debouncers=this._debouncers||{};let debouncer=this._debouncers[jobName];return!!(debouncer&&debouncer.isActive())}flushDebouncer(jobName){this._debouncers=this._debouncers||{};let debouncer=this._debouncers[jobName];if(debouncer){debouncer.flush()}}cancelDebouncer(jobName){this._debouncers=this._debouncers||{};let debouncer=this._debouncers[jobName];if(debouncer){debouncer.cancel()}}async(callback,waitTime){return waitTime>0?timeOut.run(callback.bind(this),waitTime):~microTask.run(callback.bind(this))}cancelAsync(handle){handle<0?microTask.cancel(~handle):timeOut.cancel(handle)}create(tag,props){let elt=document.createElement(tag);if(props){if(elt.setProperties){elt.setProperties(props)}else{for(let n in props){elt[n]=props[n]}}}return elt}elementMatches(selector,node){return matchesSelector(node||this,selector)}toggleAttribute(name,bool){let node=this;if(arguments.length===3){node=arguments[2]}if(arguments.length==1){bool=!node.hasAttribute(name)}if(bool){wrap(node).setAttribute(name,"");return true}else{wrap(node).removeAttribute(name);return false}}toggleClass(name,bool,node){node=node||this;if(arguments.length==1){bool=!node.classList.contains(name)}if(bool){node.classList.add(name)}else{node.classList.remove(name)}}transform(transformText,node){node=node||this;node.style.webkitTransform=transformText;node.style.transform=transformText}translate3d(x,y,z,node){node=node||this;this.transform("translate3d("+x+","+y+","+z+")",node)}arrayDelete(arrayOrPath,item){let index;if(Array.isArray(arrayOrPath)){index=arrayOrPath.indexOf(item);if(index>=0){return arrayOrPath.splice(index,1)}}else{let arr=get(this,arrayOrPath);index=arr.indexOf(item);if(index>=0){return this.splice(arrayOrPath,index,1)}}return null}_logger(level,args){if(Array.isArray(args)&&args.length===1&&Array.isArray(args[0])){args=args[0]}switch(level){case"log":case"warn":case"error":console[level](...args)}}_log(...args){this._logger("log",args)}_warn(...args){this._logger("warn",args)}_error(...args){this._logger("error",args)}_logf(methodName,...args){return["[%s::%s]",this.is,methodName,...args]}}LegacyElement.prototype.is="";return LegacyElement});const lifecycleProps={attached:true,detached:true,ready:true,created:true,beforeRegister:true,registered:true,attributeChanged:true,listeners:true,hostAttributes:true};const excludeOnInfo={attached:true,detached:true,ready:true,created:true,beforeRegister:true,registered:true,attributeChanged:true,behaviors:true,_noAccessors:true};const excludeOnBehaviors=Object.assign({listeners:true,hostAttributes:true,properties:true,observers:true},excludeOnInfo);function copyProperties(source,target,excludeProps){const noAccessors=source._noAccessors;const propertyNames=Object.getOwnPropertyNames(source);for(let i=0;i<propertyNames.length;i++){let p=propertyNames[i];if(p in excludeProps){continue}if(noAccessors){target[p]=source[p]}else{let pd=Object.getOwnPropertyDescriptor(source,p);if(pd){pd.configurable=true;Object.defineProperty(target,p,pd)}}}}function applyBehaviors(proto,behaviors,lifecycle){for(let i=0;i<behaviors.length;i++){applyInfo(proto,behaviors[i],lifecycle,excludeOnBehaviors)}}function applyInfo(proto,info,lifecycle,excludeProps){copyProperties(info,proto,excludeProps);for(let p in lifecycleProps){if(info[p]){lifecycle[p]=lifecycle[p]||[];lifecycle[p].push(info[p])}}}function flattenBehaviors(behaviors,list,exclude){list=list||[];for(let i=behaviors.length-1;i>=0;i--){let b=behaviors[i];if(b){if(Array.isArray(b)){flattenBehaviors(b,list)}else{if(list.indexOf(b)<0&&(!exclude||exclude.indexOf(b)<0)){list.unshift(b)}}}else{console.warn("behavior is null, check for missing or 404 import")}}return list}function mergeProperties(target,source){for(const p in source){const targetInfo=target[p];const sourceInfo=source[p];if(!("value"in sourceInfo)&&targetInfo&&"value"in targetInfo){target[p]=Object.assign({value:targetInfo.value},sourceInfo)}else{target[p]=sourceInfo}}}function GenerateClassFromInfo(info,Base,behaviors){let behaviorList;const lifecycle={};class PolymerGenerated extends Base{static _finalizeClass(){if(!this.hasOwnProperty(JSCompiler_renameProperty("generatedFrom",this))){super._finalizeClass()}else{if(behaviorList){for(let i=0,b;i<behaviorList.length;i++){b=behaviorList[i];if(b.properties){this.createProperties(b.properties)}if(b.observers){this.createObservers(b.observers,b.properties)}}}if(info.properties){this.createProperties(info.properties)}if(info.observers){this.createObservers(info.observers,info.properties)}this._prepareTemplate()}}static get properties(){const properties={};if(behaviorList){for(let i=0;i<behaviorList.length;i++){mergeProperties(properties,behaviorList[i].properties)}}mergeProperties(properties,info.properties);return properties}static get observers(){let observers=[];if(behaviorList){for(let i=0,b;i<behaviorList.length;i++){b=behaviorList[i];if(b.observers){observers=observers.concat(b.observers)}}}if(info.observers){observers=observers.concat(info.observers)}return observers}created(){super.created();const list=lifecycle.created;if(list){for(let i=0;i<list.length;i++){list[i].call(this)}}}_registered(){const generatedProto=PolymerGenerated.prototype;if(!generatedProto.hasOwnProperty("__hasRegisterFinished")){generatedProto.__hasRegisterFinished=true;super._registered();if(legacyOptimizations){copyPropertiesToProto(generatedProto)}const proto=Object.getPrototypeOf(this);let list=lifecycle.beforeRegister;if(list){for(let i=0;i<list.length;i++){list[i].call(proto)}}list=lifecycle.registered;if(list){for(let i=0;i<list.length;i++){list[i].call(proto)}}}}_applyListeners(){super._applyListeners();const list=lifecycle.listeners;if(list){for(let i=0;i<list.length;i++){const listeners=list[i];if(listeners){for(let l in listeners){this._addMethodEventListenerToNode(this,l,listeners[l])}}}}}_ensureAttributes(){const list=lifecycle.hostAttributes;if(list){for(let i=list.length-1;i>=0;i--){const hostAttributes=list[i];for(let a in hostAttributes){this._ensureAttribute(a,hostAttributes[a])}}}super._ensureAttributes()}ready(){super.ready();let list=lifecycle.ready;if(list){for(let i=0;i<list.length;i++){list[i].call(this)}}}attached(){super.attached();let list=lifecycle.attached;if(list){for(let i=0;i<list.length;i++){list[i].call(this)}}}detached(){super.detached();let list=lifecycle.detached;if(list){for(let i=0;i<list.length;i++){list[i].call(this)}}}attributeChanged(name,old,value){super.attributeChanged();let list=lifecycle.attributeChanged;if(list){for(let i=0;i<list.length;i++){list[i].call(this,name,old,value)}}}}if(behaviors){if(!Array.isArray(behaviors)){behaviors=[behaviors]}let superBehaviors=Base.prototype.behaviors;behaviorList=flattenBehaviors(behaviors,null,superBehaviors);PolymerGenerated.prototype.behaviors=superBehaviors?superBehaviors.concat(behaviors):behaviorList}const copyPropertiesToProto=proto=>{if(behaviorList){applyBehaviors(proto,behaviorList,lifecycle)}applyInfo(proto,info,lifecycle,excludeOnInfo)};if(!legacyOptimizations){copyPropertiesToProto(PolymerGenerated.prototype)}PolymerGenerated.generatedFrom=info;return PolymerGenerated}const Class=function(info,mixin){if(!info){console.warn("Polymer.Class requires `info` argument")}let klass=mixin?mixin(LegacyElementMixin(HTMLElement)):LegacyElementMixin(HTMLElement);klass=GenerateClassFromInfo(info,klass,info.behaviors);klass.is=klass.prototype.is=info.is;return klass};const Polymer=function(info){let klass;if(typeof info==="function"){klass=info}else{klass=Polymer.Class(info)}customElements.define(klass.is,klass);return klass};Polymer.Class=Class;function mutablePropertyChange(inst,property,value,old,mutableData){let isObject;if(mutableData){isObject=typeof value==="object"&&value!==null;if(isObject){old=inst.__dataTemp[property]}}let shouldChange=old!==value&&(old===old||value===value);if(isObject&&shouldChange){inst.__dataTemp[property]=value}return shouldChange}const MutableData=dedupingMixin(superClass=>{class MutableData extends superClass{_shouldPropertyChange(property,value,old){return mutablePropertyChange(this,property,value,old,true)}}return MutableData});const OptionalMutableData=dedupingMixin(superClass=>{class OptionalMutableData extends superClass{static get properties(){return{mutableData:Boolean}}_shouldPropertyChange(property,value,old){return mutablePropertyChange(this,property,value,old,this.mutableData)}}return OptionalMutableData});MutableData._mutablePropertyChange=mutablePropertyChange;let newInstance=null;function HTMLTemplateElementExtension(){return newInstance}HTMLTemplateElementExtension.prototype=Object.create(HTMLTemplateElement.prototype,{constructor:{value:HTMLTemplateElementExtension,writable:true}});const DataTemplate=PropertyEffects(HTMLTemplateElementExtension);const MutableDataTemplate=MutableData(DataTemplate);function upgradeTemplate(template,constructor){newInstance=template;Object.setPrototypeOf(template,constructor.prototype);new constructor;newInstance=null}const templateInstanceBase=PropertyEffects(class{});class TemplateInstanceBase extends templateInstanceBase{constructor(props){super();this._configureProperties(props);this.root=this._stampTemplate(this.__dataHost);let children=this.children=[];for(let n=this.root.firstChild;n;n=n.nextSibling){children.push(n);n.__templatizeInstance=this}if(this.__templatizeOwner&&this.__templatizeOwner.__hideTemplateChildren__){this._showHideChildren(true)}let options=this.__templatizeOptions;if(props&&options.instanceProps||!options.instanceProps){this._enableProperties()}}_configureProperties(props){let options=this.__templatizeOptions;if(options.forwardHostProp){for(let hprop in this.__hostProps){this._setPendingProperty(hprop,this.__dataHost["_host_"+hprop])}}for(let iprop in props){this._setPendingProperty(iprop,props[iprop])}}forwardHostProp(prop,value){if(this._setPendingPropertyOrPath(prop,value,false,true)){this.__dataHost._enqueueClient(this)}}_addEventListenerToNode(node,eventName,handler){if(this._methodHost&&this.__templatizeOptions.parentModel){this._methodHost._addEventListenerToNode(node,eventName,e=>{e.model=this;handler(e)})}else{let templateHost=this.__dataHost.__dataHost;if(templateHost){templateHost._addEventListenerToNode(node,eventName,handler)}}}_showHideChildren(hide){let c=this.children;for(let i=0;i<c.length;i++){let n=c[i];if(Boolean(hide)!=Boolean(n.__hideTemplateChildren__)){if(n.nodeType===Node.TEXT_NODE){if(hide){n.__polymerTextContent__=n.textContent;n.textContent=""}else{n.textContent=n.__polymerTextContent__}}else if(n.localName==="slot"){if(hide){n.__polymerReplaced__=document.createComment("hidden-slot");wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__,n)}else{const replace=n.__polymerReplaced__;if(replace){wrap(wrap(replace).parentNode).replaceChild(n,replace)}}}else if(n.style){if(hide){n.__polymerDisplay__=n.style.display;n.style.display="none"}else{n.style.display=n.__polymerDisplay__}}}n.__hideTemplateChildren__=hide;if(n._showHideChildren){n._showHideChildren(hide)}}}_setUnmanagedPropertyToNode(node,prop,value){if(node.__hideTemplateChildren__&&node.nodeType==Node.TEXT_NODE&&prop=="textContent"){node.__polymerTextContent__=value}else{super._setUnmanagedPropertyToNode(node,prop,value)}}get parentModel(){let model=this.__parentModel;if(!model){let options;model=this;do{model=model.__dataHost.__dataHost}while((options=model.__templatizeOptions)&&!options.parentModel);this.__parentModel=model}return model}dispatchEvent(event){return true}}TemplateInstanceBase.prototype.__dataHost;TemplateInstanceBase.prototype.__templatizeOptions;TemplateInstanceBase.prototype._methodHost;TemplateInstanceBase.prototype.__templatizeOwner;TemplateInstanceBase.prototype.__hostProps;const MutableTemplateInstanceBase=MutableData(TemplateInstanceBase);function findMethodHost(template){let templateHost=template.__dataHost;return templateHost&&templateHost._methodHost||templateHost}function createTemplatizerClass(template,templateInfo,options){let templatizerBase=options.mutableData?MutableTemplateInstanceBase:TemplateInstanceBase;if(templatize.mixin){templatizerBase=templatize.mixin(templatizerBase)}let klass=class extends templatizerBase{};klass.prototype.__templatizeOptions=options;klass.prototype._bindTemplate(template);addNotifyEffects(klass,template,templateInfo,options);return klass}function addPropagateEffects(template,templateInfo,options){let userForwardHostProp=options.forwardHostProp;if(userForwardHostProp){let klass=templateInfo.templatizeTemplateClass;if(!klass){let templatizedBase=options.mutableData?MutableDataTemplate:DataTemplate;klass=templateInfo.templatizeTemplateClass=class TemplatizedTemplate extends templatizedBase{};let hostProps=templateInfo.hostProps;for(let prop in hostProps){klass.prototype._addPropertyEffect("_host_"+prop,klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,{fn:createForwardHostPropEffect(prop,userForwardHostProp)});klass.prototype._createNotifyingProperty("_host_"+prop)}}upgradeTemplate(template,klass);if(template.__dataProto){Object.assign(template.__data,template.__dataProto)}template.__dataTemp={};template.__dataPending=null;template.__dataOld=null;template._enableProperties()}}function createForwardHostPropEffect(hostProp,userForwardHostProp){return function forwardHostProp(template,prop,props){userForwardHostProp.call(template.__templatizeOwner,prop.substring("_host_".length),props[prop])}}function addNotifyEffects(klass,template,templateInfo,options){let hostProps=templateInfo.hostProps||{};for(let iprop in options.instanceProps){delete hostProps[iprop];let userNotifyInstanceProp=options.notifyInstanceProp;if(userNotifyInstanceProp){klass.prototype._addPropertyEffect(iprop,klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:createNotifyInstancePropEffect(iprop,userNotifyInstanceProp)})}}if(options.forwardHostProp&&template.__dataHost){for(let hprop in hostProps){klass.prototype._addPropertyEffect(hprop,klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:createNotifyHostPropEffect()})}}}function createNotifyInstancePropEffect(instProp,userNotifyInstanceProp){return function notifyInstanceProp(inst,prop,props){userNotifyInstanceProp.call(inst.__templatizeOwner,inst,prop,props[prop])}}function createNotifyHostPropEffect(){return function notifyHostProp(inst,prop,props){inst.__dataHost._setPendingPropertyOrPath("_host_"+prop,props[prop],true,true)}}function templatize(template,owner,options){if(strictTemplatePolicy&&!findMethodHost(template)){throw new Error("strictTemplatePolicy: template owner not trusted")}options=options||{};if(template.__templatizeOwner){throw new Error("A <template> can only be templatized once")}template.__templatizeOwner=owner;const ctor=owner?owner.constructor:TemplateInstanceBase;let templateInfo=ctor._parseTemplate(template);let baseClass=templateInfo.templatizeInstanceClass;if(!baseClass){baseClass=createTemplatizerClass(template,templateInfo,options);templateInfo.templatizeInstanceClass=baseClass}addPropagateEffects(template,templateInfo,options);let klass=class TemplateInstance extends baseClass{};klass.prototype._methodHost=findMethodHost(template);klass.prototype.__dataHost=template;klass.prototype.__templatizeOwner=owner;klass.prototype.__hostProps=templateInfo.hostProps;klass=klass;return klass}function modelForElement(template,node){let model;while(node){if(model=node.__templatizeInstance){if(model.__dataHost!=template){node=model.__dataHost}else{return model}}else{node=wrap(node).parentNode}}return null}const Templatizer={templatize(template,mutableData){this._templatizerTemplate=template;this.ctor=templatize(template,this,{mutableData:Boolean(mutableData),parentModel:this._parentModel,instanceProps:this._instanceProps,forwardHostProp:this._forwardHostPropV2,notifyInstanceProp:this._notifyInstancePropV2})},stamp(model){return new this.ctor(model)},modelForElement(el){return modelForElement(this._templatizerTemplate,el)}};const domBindBase=GestureEventListeners(OptionalMutableData(PropertyEffects(HTMLElement)));class DomBind extends domBindBase{static get observedAttributes(){return["mutable-data"]}constructor(){super();if(strictTemplatePolicy){throw new Error(`strictTemplatePolicy: dom-bind not allowed`)}this.root=null;this.$=null;this.__children=null}attributeChangedCallback(){this.mutableData=true}connectedCallback(){this.style.display="none";this.render()}disconnectedCallback(){this.__removeChildren()}__insertChildren(){wrap(wrap(this).parentNode).insertBefore(this.root,this)}__removeChildren(){if(this.__children){for(let i=0;i<this.__children.length;i++){this.root.appendChild(this.__children[i])}}}render(){let template;if(!this.__children){template=template||this.querySelector("template");if(!template){let observer=new MutationObserver(()=>{template=this.querySelector("template");if(template){observer.disconnect();this.render()}else{throw new Error("dom-bind requires a <template> child")}});observer.observe(this,{childList:true});return}this.root=this._stampTemplate(template);this.$=this.root.$;this.__children=[];for(let n=this.root.firstChild;n;n=n.nextSibling){this.__children[this.__children.length]=n}this._enableProperties()}this.__insertChildren();this.dispatchEvent(new CustomEvent("dom-change",{bubbles:true,composed:true}))}}customElements.define("dom-bind",DomBind);class LiteralString{constructor(string){this.value=string.toString()}toString(){return this.value}}function literalValue(value){if(value instanceof LiteralString){return value.value}else{throw new Error(`non-literal value passed to Polymer's htmlLiteral function: ${value}`)}}function htmlValue(value){if(value instanceof HTMLTemplateElement){return value.innerHTML}else if(value instanceof LiteralString){return literalValue(value)}else{throw new Error(`non-template value passed to Polymer's html function: ${value}`)}}const html=function html(strings,...values){const template=document.createElement("template");template.innerHTML=values.reduce((acc,v,idx)=>acc+htmlValue(v)+strings[idx+1],strings[0]);return template};const PolymerElement=ElementMixin(HTMLElement);const domRepeatBase=OptionalMutableData(PolymerElement);class DomRepeat extends domRepeatBase{static get is(){return"dom-repeat"}static get template(){return null}static get properties(){return{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},itemsIndexAs:{type:String,value:"itemsIndex"},sort:{type:Function,observer:"__sortChanged"},filter:{type:Function,observer:"__filterChanged"},observe:{type:String,observer:"__observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:true,readOnly:true},initialCount:{type:Number,observer:"__initializeChunking"},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"__computeFrameTime(targetFramerate)"}}}static get observers(){return["__itemsChanged(items.*)"]}constructor(){super();this.__instances=[];this.__limit=Infinity;this.__pool=[];this.__renderDebouncer=null;this.__itemsIdxToInstIdx={};this.__chunkCount=null;this.__lastChunkTime=null;this.__sortFn=null;this.__filterFn=null;this.__observePaths=null;this.__ctor=null;this.__isDetached=true;this.template=null}disconnectedCallback(){super.disconnectedCallback();this.__isDetached=true;for(let i=0;i<this.__instances.length;i++){this.__detachInstance(i)}}connectedCallback(){super.connectedCallback();this.style.display="none";if(this.__isDetached){this.__isDetached=false;let wrappedParent=wrap(wrap(this).parentNode);for(let i=0;i<this.__instances.length;i++){this.__attachInstance(i,wrappedParent)}}}__ensureTemplatized(){if(!this.__ctor){let template=this.template=this.querySelector("template");if(!template){let observer=new MutationObserver(()=>{if(this.querySelector("template")){observer.disconnect();this.__render()}else{throw new Error("dom-repeat requires a <template> child")}});observer.observe(this,{childList:true});return false}let instanceProps={};instanceProps[this.as]=true;instanceProps[this.indexAs]=true;instanceProps[this.itemsIndexAs]=true;this.__ctor=templatize(template,this,{mutableData:this.mutableData,parentModel:true,instanceProps:instanceProps,forwardHostProp:function(prop,value){let i$=this.__instances;for(let i=0,inst;i<i$.length&&(inst=i$[i]);i++){inst.forwardHostProp(prop,value)}},notifyInstanceProp:function(inst,prop,value){if(matches(this.as,prop)){let idx=inst[this.itemsIndexAs];if(prop==this.as){this.items[idx]=value}let path=translate(this.as,`${JSCompiler_renameProperty("items",this)}.${idx}`,prop);this.notifyPath(path,value)}}})}return true}__getMethodHost(){return this.__dataHost._methodHost||this.__dataHost}__functionFromPropertyValue(functionOrMethodName){if(typeof functionOrMethodName==="string"){let methodName=functionOrMethodName;let obj=this.__getMethodHost();return function(){return obj[methodName].apply(obj,arguments)}}return functionOrMethodName}__sortChanged(sort){this.__sortFn=this.__functionFromPropertyValue(sort);if(this.items){this.__debounceRender(this.__render)}}__filterChanged(filter){this.__filterFn=this.__functionFromPropertyValue(filter);if(this.items){this.__debounceRender(this.__render)}}__computeFrameTime(rate){return Math.ceil(1e3/rate)}__initializeChunking(){if(this.initialCount){this.__limit=this.initialCount;this.__chunkCount=this.initialCount;this.__lastChunkTime=performance.now()}}__tryRenderChunk(){if(this.items&&this.__limit<this.items.length){this.__debounceRender(this.__requestRenderChunk)}}__requestRenderChunk(){requestAnimationFrame(()=>this.__renderChunk())}__renderChunk(){let currChunkTime=performance.now();let ratio=this._targetFrameTime/(currChunkTime-this.__lastChunkTime);this.__chunkCount=Math.round(this.__chunkCount*ratio)||1;this.__limit+=this.__chunkCount;this.__lastChunkTime=currChunkTime;this.__debounceRender(this.__render)}__observeChanged(){this.__observePaths=this.observe&&this.observe.replace(".*",".").split(" ")}__itemsChanged(change){if(this.items&&!Array.isArray(this.items)){console.warn("dom-repeat expected array for `items`, found",this.items)}if(!this.__handleItemPath(change.path,change.value)){this.__initializeChunking();this.__debounceRender(this.__render)}}__handleObservedPaths(path){if(this.__sortFn||this.__filterFn){if(!path){this.__debounceRender(this.__render,this.delay)}else if(this.__observePaths){let paths=this.__observePaths;for(let i=0;i<paths.length;i++){if(path.indexOf(paths[i])===0){this.__debounceRender(this.__render,this.delay)}}}}}__debounceRender(fn,delay=0){this.__renderDebouncer=Debouncer.debounce(this.__renderDebouncer,delay>0?timeOut.after(delay):microTask,fn.bind(this));enqueueDebouncer(this.__renderDebouncer)}render(){this.__debounceRender(this.__render);flush()}__render(){if(!this.__ensureTemplatized()){return}this.__applyFullRefresh();this.__pool.length=0;this._setRenderedItemCount(this.__instances.length);this.dispatchEvent(new CustomEvent("dom-change",{bubbles:true,composed:true}));this.__tryRenderChunk()}__applyFullRefresh(){let items=this.items||[];let isntIdxToItemsIdx=new Array(items.length);for(let i=0;i<items.length;i++){isntIdxToItemsIdx[i]=i}if(this.__filterFn){isntIdxToItemsIdx=isntIdxToItemsIdx.filter((i,idx,array)=>this.__filterFn(items[i],idx,array))}if(this.__sortFn){isntIdxToItemsIdx.sort((a,b)=>this.__sortFn(items[a],items[b]))}const itemsIdxToInstIdx=this.__itemsIdxToInstIdx={};let instIdx=0;const limit=Math.min(isntIdxToItemsIdx.length,this.__limit);for(;instIdx<limit;instIdx++){let inst=this.__instances[instIdx];let itemIdx=isntIdxToItemsIdx[instIdx];let item=items[itemIdx];itemsIdxToInstIdx[itemIdx]=instIdx;if(inst){inst._setPendingProperty(this.as,item);inst._setPendingProperty(this.indexAs,instIdx);inst._setPendingProperty(this.itemsIndexAs,itemIdx);inst._flushProperties()}else{this.__insertInstance(item,instIdx,itemIdx)}}for(let i=this.__instances.length-1;i>=instIdx;i--){this.__detachAndRemoveInstance(i)}}__detachInstance(idx){let inst=this.__instances[idx];const wrappedRoot=wrap(inst.root);for(let i=0;i<inst.children.length;i++){let el=inst.children[i];wrappedRoot.appendChild(el)}return inst}__attachInstance(idx,parent){let inst=this.__instances[idx];parent.insertBefore(inst.root,this)}__detachAndRemoveInstance(idx){let inst=this.__detachInstance(idx);if(inst){this.__pool.push(inst)}this.__instances.splice(idx,1)}__stampInstance(item,instIdx,itemIdx){let model={};model[this.as]=item;model[this.indexAs]=instIdx;model[this.itemsIndexAs]=itemIdx;return new this.__ctor(model)}__insertInstance(item,instIdx,itemIdx){let inst=this.__pool.pop();if(inst){inst._setPendingProperty(this.as,item);inst._setPendingProperty(this.indexAs,instIdx);inst._setPendingProperty(this.itemsIndexAs,itemIdx);inst._flushProperties()}else{inst=this.__stampInstance(item,instIdx,itemIdx)}let beforeRow=this.__instances[instIdx+1];let beforeNode=beforeRow?beforeRow.children[0]:this;wrap(wrap(this).parentNode).insertBefore(inst.root,beforeNode);this.__instances[instIdx]=inst;return inst}_showHideChildren(hidden){for(let i=0;i<this.__instances.length;i++){this.__instances[i]._showHideChildren(hidden)}}__handleItemPath(path,value){let itemsPath=path.slice(6);let dot=itemsPath.indexOf(".");let itemsIdx=dot<0?itemsPath:itemsPath.substring(0,dot);if(itemsIdx==parseInt(itemsIdx,10)){let itemSubPath=dot<0?"":itemsPath.substring(dot+1);this.__handleObservedPaths(itemSubPath);let instIdx=this.__itemsIdxToInstIdx[itemsIdx];let inst=this.__instances[instIdx];if(inst){let itemPath=this.as+(itemSubPath?"."+itemSubPath:"");inst._setPendingPropertyOrPath(itemPath,value,false,true);inst._flushProperties()}return true}}itemForElement(el){let instance=this.modelForElement(el);return instance&&instance[this.as]}indexForElement(el){let instance=this.modelForElement(el);return instance&&instance[this.indexAs]}modelForElement(el){return modelForElement(this.template,el)}}customElements.define(DomRepeat.is,DomRepeat);class DomIf extends PolymerElement{static get is(){return"dom-if"}static get template(){return null}static get properties(){return{if:{type:Boolean,observer:"__debounceRender"},restamp:{type:Boolean,observer:"__debounceRender"}}}constructor(){super();this.__renderDebouncer=null;this.__invalidProps=null;this.__instance=null;this._lastIf=false;this.__ctor=null;this.__hideTemplateChildren__=false}__debounceRender(){this.__renderDebouncer=Debouncer.debounce(this.__renderDebouncer,microTask,()=>this.__render());enqueueDebouncer(this.__renderDebouncer)}disconnectedCallback(){super.disconnectedCallback();const parent=wrap(this).parentNode;if(!parent||parent.nodeType==Node.DOCUMENT_FRAGMENT_NODE&&!wrap(parent).host){this.__teardownInstance()}}connectedCallback(){super.connectedCallback();this.style.display="none";if(this.if){this.__debounceRender()}}render(){flush()}__render(){if(this.if){if(!this.__ensureInstance()){return}this._showHideChildren()}else if(this.restamp){this.__teardownInstance()}if(!this.restamp&&this.__instance){this._showHideChildren()}if(this.if!=this._lastIf){this.dispatchEvent(new CustomEvent("dom-change",{bubbles:true,composed:true}));this._lastIf=this.if}}__ensureInstance(){let parentNode=wrap(this).parentNode;if(parentNode){if(!this.__ctor){let template=wrap(this).querySelector("template");if(!template){let observer=new MutationObserver(()=>{if(wrap(this).querySelector("template")){observer.disconnect();this.__render()}else{throw new Error("dom-if requires a <template> child")}});observer.observe(this,{childList:true});return false}this.__ctor=templatize(template,this,{mutableData:true,forwardHostProp:function(prop,value){if(this.__instance){if(this.if){this.__instance.forwardHostProp(prop,value)}else{this.__invalidProps=this.__invalidProps||Object.create(null);this.__invalidProps[root(prop)]=true}}}})}if(!this.__instance){this.__instance=new this.__ctor;wrap(parentNode).insertBefore(this.__instance.root,this)}else{this.__syncHostProperties();let c$=this.__instance.children;if(c$&&c$.length){let lastChild=wrap(this).previousSibling;if(lastChild!==c$[c$.length-1]){for(let i=0,n;i<c$.length&&(n=c$[i]);i++){wrap(parentNode).insertBefore(n,this)}}}}}return true}__syncHostProperties(){let props=this.__invalidProps;if(props){for(let prop in props){this.__instance._setPendingProperty(prop,this.__dataHost[prop])}this.__invalidProps=null;this.__instance._flushProperties()}}__teardownInstance(){if(this.__instance){let c$=this.__instance.children;if(c$&&c$.length){let parent=wrap(c$[0]).parentNode;if(parent){parent=wrap(parent);for(let i=0,n;i<c$.length&&(n=c$[i]);i++){parent.removeChild(n)}}}this.__instance=null;this.__invalidProps=null}}_showHideChildren(){let hidden=this.__hideTemplateChildren__||!this.if;if(this.__instance){this.__instance._showHideChildren(hidden)}}}customElements.define(DomIf.is,DomIf);let ArraySelectorMixin=dedupingMixin(superClass=>{let elementBase=ElementMixin(superClass);class ArraySelectorMixin extends elementBase{static get properties(){return{items:{type:Array},multi:{type:Boolean,value:false},selected:{type:Object,notify:true},selectedItem:{type:Object,notify:true},toggle:{type:Boolean,value:false}}}static get observers(){return["__updateSelection(multi, items.*)"]}constructor(){super();this.__lastItems=null;this.__lastMulti=null;this.__selectedMap=null}__updateSelection(multi,itemsInfo){let path=itemsInfo.path;if(path==JSCompiler_renameProperty("items",this)){let newItems=itemsInfo.base||[];let lastItems=this.__lastItems;let lastMulti=this.__lastMulti;if(multi!==lastMulti){this.clearSelection()}if(lastItems){let splices=calculateSplices(newItems,lastItems);this.__applySplices(splices)}this.__lastItems=newItems;this.__lastMulti=multi}else if(itemsInfo.path==`${JSCompiler_renameProperty("items",this)}.splices`){this.__applySplices(itemsInfo.value.indexSplices)}else{let part=path.slice(`${JSCompiler_renameProperty("items",this)}.`.length);let idx=parseInt(part,10);if(part.indexOf(".")<0&&part==idx){this.__deselectChangedIdx(idx)}}}__applySplices(splices){let selected=this.__selectedMap;for(let i=0;i<splices.length;i++){let s=splices[i];selected.forEach((idx,item)=>{if(idx<s.index);else if(idx>=s.index+s.removed.length){selected.set(item,idx+s.addedCount-s.removed.length)}else{selected.set(item,-1)}});for(let j=0;j<s.addedCount;j++){let idx=s.index+j;if(selected.has(this.items[idx])){selected.set(this.items[idx],idx)}}}this.__updateLinks();let sidx=0;selected.forEach((idx,item)=>{if(idx<0){if(this.multi){this.splice(JSCompiler_renameProperty("selected",this),sidx,1)}else{this.selected=this.selectedItem=null}selected.delete(item)}else{sidx++}})}__updateLinks(){this.__dataLinkedPaths={};if(this.multi){let sidx=0;this.__selectedMap.forEach(idx=>{if(idx>=0){this.linkPaths(`${JSCompiler_renameProperty("items",this)}.${idx}`,`${JSCompiler_renameProperty("selected",this)}.${sidx++}`)}})}else{this.__selectedMap.forEach(idx=>{this.linkPaths(JSCompiler_renameProperty("selected",this),`${JSCompiler_renameProperty("items",this)}.${idx}`);this.linkPaths(JSCompiler_renameProperty("selectedItem",this),`${JSCompiler_renameProperty("items",this)}.${idx}`)})}}clearSelection(){this.__dataLinkedPaths={};this.__selectedMap=new Map;this.selected=this.multi?[]:null;this.selectedItem=null}isSelected(item){return this.__selectedMap.has(item)}isIndexSelected(idx){return this.isSelected(this.items[idx])}__deselectChangedIdx(idx){let sidx=this.__selectedIndexForItemIndex(idx);if(sidx>=0){let i=0;this.__selectedMap.forEach((idx,item)=>{if(sidx==i++){this.deselect(item)}})}}__selectedIndexForItemIndex(idx){let selected=this.__dataLinkedPaths[`${JSCompiler_renameProperty("items",this)}.${idx}`];if(selected){return parseInt(selected.slice(`${JSCompiler_renameProperty("selected",this)}.`.length),10)}}deselect(item){let idx=this.__selectedMap.get(item);if(idx>=0){this.__selectedMap.delete(item);let sidx;if(this.multi){sidx=this.__selectedIndexForItemIndex(idx)}this.__updateLinks();if(this.multi){this.splice(JSCompiler_renameProperty("selected",this),sidx,1)}else{this.selected=this.selectedItem=null}}}deselectIndex(idx){this.deselect(this.items[idx])}select(item){this.selectIndex(this.items.indexOf(item))}selectIndex(idx){let item=this.items[idx];if(!this.isSelected(item)){if(!this.multi){this.__selectedMap.clear()}this.__selectedMap.set(item,idx);this.__updateLinks();if(this.multi){this.push(JSCompiler_renameProperty("selected",this),item)}else{this.selected=this.selectedItem=item}}else if(this.toggle){this.deselectIndex(idx)}}}return ArraySelectorMixin});let baseArraySelector=ArraySelectorMixin(PolymerElement);class ArraySelector extends baseArraySelector{static get is(){return"array-selector"}static get template(){return null}}customElements.define(ArraySelector.is,ArraySelector);const customStyleInterface=new CustomStyleInterface;if(!window.ShadyCSS){window.ShadyCSS={prepareTemplate(template,elementName,elementExtends){},prepareTemplateDom(template,elementName){},prepareTemplateStyles(template,elementName,elementExtends){},styleSubtree(element,properties){customStyleInterface.processStyles();updateNativeProperties(element,properties)},styleElement(element){customStyleInterface.processStyles()},styleDocument(properties){customStyleInterface.processStyles();updateNativeProperties(document.body,properties)},getComputedStyleValue(element,property){return getComputedStyleValue(element,property)},flushCustomStyles(){},nativeCss:nativeCssVariables,nativeShadow:nativeShadow,cssBuild:cssBuild,disableRuntime:disableRuntime}}window.ShadyCSS.CustomStyleInterface=customStyleInterface;const attr="include";const CustomStyleInterface$1=window.ShadyCSS.CustomStyleInterface;class CustomStyle extends HTMLElement{constructor(){super();this._style=null;CustomStyleInterface$1.addCustomStyle(this)}getStyle(){if(this._style){return this._style}const style=this.querySelector("style");if(!style){return null}this._style=style;const include=style.getAttribute(attr);if(include){style.removeAttribute(attr);style.textContent=cssFromModules(include)+style.textContent}if(this.ownerDocument!==window.document){window.document.head.appendChild(this)}return this._style}}window.customElements.define("custom-style",CustomStyle);let mutablePropertyChange$1;(()=>{mutablePropertyChange$1=MutableData._mutablePropertyChange})();const OptionalMutableDataBehavior={properties:{mutableData:Boolean},_shouldPropertyChange(property,value,old){return mutablePropertyChange$1(this,property,value,old,this.mutableData)}};const Base=LegacyElementMixin(HTMLElement).prototype;export{Base,Debouncer,OptionalMutableDataBehavior,Polymer,PolymerElement,TemplateInstanceBase,Templatizer,animationFrame,dashToCamelCase,dom,enqueueDebouncer,flush,gestures$1 as gestures,html,idlePeriod,matches,microTask,translate,useShadow};
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.html b/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.html
deleted file mode 100644
index d344ec3..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!--
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--->
-<script src="apply-shim.min.js"></script>
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.min.js b/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.min.js
deleted file mode 100644
index fb2ddee..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.min.js
+++ /dev/null
@@ -1,32 +0,0 @@
-(function(){/*
-
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-'use strict';var l=!(window.ShadyDOM&&window.ShadyDOM.inUse),p;function r(a){p=a&&a.shimcssproperties?!1:l||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var t;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(t=window.ShadyCSS.cssBuild);var aa=!(!window.ShadyCSS||!window.ShadyCSS.disableRuntime);
-window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?p=window.ShadyCSS.nativeCss:window.ShadyCSS?(r(window.ShadyCSS),window.ShadyCSS=void 0):r(window.WebComponents&&window.WebComponents.flags);var u=p,v=t;function w(){this.end=this.start=0;this.rules=this.parent=this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""}
-function x(a){a=a.replace(ba,"").replace(ca,"");var b=y,c=a,e=new w;e.start=0;e.end=c.length;for(var d=e,f=0,g=c.length;f<g;f++)if("{"===c[f]){d.rules||(d.rules=[]);var h=d,k=h.rules[h.rules.length-1]||null;d=new w;d.start=f+1;d.parent=h;d.previous=k;h.rules.push(d)}else"}"===c[f]&&(d.end=f+1,d=d.parent||e);return b(e,a)}
-function y(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=da(c),c=c.replace(z," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=A:c.match(ea)&&(a.type=B,a.keyframesName=a.selector.split(z).pop()):a.type=0===c.indexOf("--")?C:D);if(c=a.rules)for(var e=0,d=c.length,f=void 0;e<d&&(f=c[e]);e++)y(f,b);
-return a}function da(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})}
-function E(a,b,c){c=void 0===c?"":c;var e="";if(a.cssText||a.rules){var d=a.rules,f;if(f=d)f=d[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var g=d.length,h=void 0;f<g&&(h=d[f]);f++)e=E(h,b,e)}else b?b=a.cssText:(b=a.cssText,b=b.replace(fa,"").replace(ha,""),b=b.replace(ia,"").replace(ja,"")),(e=b.trim())&&(e="  "+e+"\n")}e&&(a.selector&&(c+=a.selector+" {\n"),c+=e,a.selector&&(c+="}\n\n"));return c}
-var D=1,B=7,A=4,C=1E3,ba=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,ca=/@import[^;]*;/gim,fa=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,ha=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,ia=/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,ja=/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,ea=/^@[^\s]*keyframes/,z=/\s+/g;var G=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi,H=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,ka=/@media\s(.*)/;var I=new Set;function J(a){if(!a)return"";"string"===typeof a&&(a=x(a));return E(a,u)}function K(a){!a.__cssRules&&a.textContent&&(a.__cssRules=x(a.textContent));return a.__cssRules||null}function L(a,b,c,e){if(a){var d=!1,f=a.type;if(e&&f===A){var g=a.selector.match(ka);g&&(window.matchMedia(g[1]).matches||(d=!0))}f===D?b(a):c&&f===B?c(a):f===C&&(d=!0);if((a=a.rules)&&!d)for(d=0,f=a.length,g=void 0;d<f&&(g=a[d]);d++)L(g,b,c,e)}}
-function M(a,b){var c=a.indexOf("var(");if(-1===c)return b(a,"","","");a:{var e=0;var d=c+3;for(var f=a.length;d<f;d++)if("("===a[d])e++;else if(")"===a[d]&&0===--e)break a;d=-1}e=a.substring(c+4,d);c=a.substring(0,c);a=M(a.substring(d+1),b);d=e.indexOf(",");return-1===d?b(c,e.trim(),"",a):b(c,e.substring(0,d).trim(),e.substring(d+1).trim(),a)}
-function N(a){if(void 0!==v)return v;if(void 0===a.__cssBuild){var b=a.getAttribute("css-build");if(b)a.__cssBuild=b;else{a:{b="template"===a.localName?a.content.firstChild:a.firstChild;if(b instanceof Comment&&(b=b.textContent.trim().split(":"),"css-build"===b[0])){b=b[1];break a}b=""}if(""!==b){var c="template"===a.localName?a.content.firstChild:a.firstChild;c.parentNode.removeChild(c)}a.__cssBuild=b}}return a.__cssBuild||""};var la=/;\s*/m,ma=/^\s*(initial)|(inherit)\s*$/,O=/\s*!important/;function P(){this.a={}}P.prototype.set=function(a,b){a=a.trim();this.a[a]={h:b,i:{}}};P.prototype.get=function(a){a=a.trim();return this.a[a]||null};var Q=null;function R(){this.b=this.c=null;this.a=new P}R.prototype.o=function(a){a=H.test(a)||G.test(a);H.lastIndex=0;G.lastIndex=0;return a};
-R.prototype.m=function(a,b){if(void 0===a._gatheredStyle){var c=[];for(var e=a.content.querySelectorAll("style"),d=0;d<e.length;d++){var f=e[d];if(f.hasAttribute("shady-unscoped")){if(!l){var g=f.textContent;I.has(g)||(I.add(g),g=f.cloneNode(!0),document.head.appendChild(g));f.parentNode.removeChild(f)}}else c.push(f.textContent),f.parentNode.removeChild(f)}(c=c.join("").trim())?(e=document.createElement("style"),e.textContent=c,a.content.insertBefore(e,a.content.firstChild),c=e):c=null;a._gatheredStyle=
-c}return(a=a._gatheredStyle)?this.j(a,b):null};R.prototype.j=function(a,b){b=void 0===b?"":b;var c=K(a);this.l(c,b);a.textContent=J(c);return c};R.prototype.f=function(a){var b=this,c=K(a);L(c,function(a){":root"===a.selector&&(a.selector="html");b.g(a)});a.textContent=J(c);return c};R.prototype.l=function(a,b){var c=this;this.c=b;L(a,function(a){c.g(a)});this.c=null};R.prototype.g=function(a){a.cssText=na(this,a.parsedCssText,a);":root"===a.selector&&(a.selector=":host > *")};
-function na(a,b,c){b=b.replace(G,function(b,d,f,g){return oa(a,b,d,f,g,c)});return S(a,b,c)}function pa(a,b){for(var c=b;c.parent;)c=c.parent;var e={},d=!1;L(c,function(c){(d=d||c===b)||c.selector===b.selector&&Object.assign(e,T(a,c.parsedCssText))});return e}
-function S(a,b,c){for(var e;e=H.exec(b);){var d=e[0],f=e[1];e=e.index;var g=b.slice(0,e+d.indexOf("@apply"));b=b.slice(e+d.length);var h=c?pa(a,c):{};Object.assign(h,T(a,g));d=void 0;var k=a;f=f.replace(la,"");var n=[];var m=k.a.get(f);m||(k.a.set(f,{}),m=k.a.get(f));if(m){k.c&&(m.i[k.c]=!0);var q=m.h;for(d in q)k=h&&h[d],m=[d,": var(",f,"_-_",d],k&&m.push(",",k.replace(O,"")),m.push(")"),O.test(q[d])&&m.push(" !important"),n.push(m.join(""))}d=n.join("; ");b=g+d+b;H.lastIndex=e+d.length}return b}
-function T(a,b,c){c=void 0===c?!1:c;b=b.split(";");for(var e,d,f={},g=0,h;g<b.length;g++)if(e=b[g])if(h=e.split(":"),1<h.length){e=h[0].trim();d=h.slice(1).join(":");if(c){var k=a;h=e;var n=ma.exec(d);n&&(n[1]?(k.b||(k.b=document.createElement("meta"),k.b.setAttribute("apply-shim-measure",""),k.b.style.all="initial",document.head.appendChild(k.b)),h=window.getComputedStyle(k.b).getPropertyValue(h)):h="apply-shim-inherit",d=h)}f[e]=d}return f}function qa(a,b){if(Q)for(var c in b.i)c!==a.c&&Q(c)}
-function oa(a,b,c,e,d,f){e&&M(e,function(b,c){c&&a.a.get(c)&&(d="@apply "+c+";")});if(!d)return b;var g=S(a,""+d,f);f=b.slice(0,b.indexOf("--"));var h=g=T(a,g,!0),k=a.a.get(c),n=k&&k.h;n?h=Object.assign(Object.create(n),g):a.a.set(c,h);var m=[],q,Z=!1;for(q in h){var F=g[q];void 0===F&&(F="initial");!n||q in n||(Z=!0);m.push(c+"_-_"+q+": "+F)}Z&&qa(a,k);k&&(k.h=h);e&&(f=b+";"+f);return f+m.join("; ")+";"}R.prototype.detectMixin=R.prototype.o;R.prototype.transformStyle=R.prototype.j;
-R.prototype.transformCustomStyle=R.prototype.f;R.prototype.transformRules=R.prototype.l;R.prototype.transformRule=R.prototype.g;R.prototype.transformTemplate=R.prototype.m;R.prototype._separator="_-_";Object.defineProperty(R.prototype,"invalidCallback",{get:function(){return Q},set:function(a){Q=a}});var U={};var ra=Promise.resolve();function sa(a){if(a=U[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function ta(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function ua(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a._validating||(a._validating=!0,ra.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a._validating=!1}))};var V=new R;function W(){this.a=null;V.invalidCallback=sa}function X(a){!a.a&&window.ShadyCSS.CustomStyleInterface&&(a.a=window.ShadyCSS.CustomStyleInterface,a.a.transformCallback=function(a){V.f(a)},a.a.validateCallback=function(){requestAnimationFrame(function(){a.a.enqueued&&a.flushCustomStyles()})})}W.prototype.prepareTemplate=function(a,b){X(this);""===N(a)&&(U[b]=a,b=V.m(a,b),a._styleAst=b)};
-W.prototype.flushCustomStyles=function(){X(this);if(this.a){var a=this.a.processStyles();if(this.a.enqueued){for(var b=0;b<a.length;b++){var c=this.a.getStyleForCustomStyle(a[b]);c&&V.f(c)}this.a.enqueued=!1}}};
-W.prototype.styleSubtree=function(a,b){X(this);if(b)for(var c in b)null===c?a.style.removeProperty(c):a.style.setProperty(c,b[c]);if(a.shadowRoot)for(this.styleElement(a),a=a.shadowRoot.children||a.shadowRoot.childNodes,b=0;b<a.length;b++)this.styleSubtree(a[b]);else for(a=a.children||a.childNodes,b=0;b<a.length;b++)this.styleSubtree(a[b])};
-W.prototype.styleElement=function(a){X(this);var b=a.localName,c;b?-1<b.indexOf("-")?c=b:c=a.getAttribute&&a.getAttribute("is")||"":c=a.is;b=U[c];if(!(b&&""!==N(b)||!b||ta(b))){if(ta(b)||b._applyShimValidatingVersion!==b._applyShimNextVersion)this.prepareTemplate(b,c),ua(b);if(a=a.shadowRoot)if(a=a.querySelector("style"))a.__cssRules=b._styleAst,a.textContent=J(b._styleAst)}};W.prototype.styleDocument=function(a){X(this);this.styleSubtree(document.body,a)};
-if(!window.ShadyCSS||!window.ShadyCSS.ScopingShim){var Y=new W,va=window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface;window.ShadyCSS={prepareTemplate:function(a,b){Y.flushCustomStyles();Y.prepareTemplate(a,b)},prepareTemplateStyles:function(a,b,c){window.ShadyCSS.prepareTemplate(a,b,c)},prepareTemplateDom:function(){},styleSubtree:function(a,b){Y.flushCustomStyles();Y.styleSubtree(a,b)},styleElement:function(a){Y.flushCustomStyles();Y.styleElement(a)},styleDocument:function(a){Y.flushCustomStyles();
-Y.styleDocument(a)},getComputedStyleValue:function(a,b){return(a=window.getComputedStyle(a).getPropertyValue(b))?a.trim():""},flushCustomStyles:function(){Y.flushCustomStyles()},nativeCss:u,nativeShadow:l,cssBuild:v,disableRuntime:aa};va&&(window.ShadyCSS.CustomStyleInterface=va)}window.ShadyCSS.ApplyShim=V;}).call(this);
-
-//# sourceMappingURL=apply-shim.min.js.map
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.html b/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.html
deleted file mode 100644
index a3919fa..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!--
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--->
-<script src="custom-style-interface.min.js"></script>
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.min.js b/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.min.js
deleted file mode 100644
index fb31c84..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.min.js
+++ /dev/null
@@ -1,15 +0,0 @@
-(function(){/*
-
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-'use strict';var c=null,f=window.HTMLImports&&window.HTMLImports.whenReady||null,g;function h(a){requestAnimationFrame(function(){f?f(a):(c||(c=new Promise(function(a){g=a}),"complete"===document.readyState?g():document.addEventListener("readystatechange",function(){"complete"===document.readyState&&g()})),c.then(function(){a&&a()}))})};var k=null,l=null;function m(){this.customStyles=[];this.enqueued=!1;h(function(){window.ShadyCSS.flushCustomStyles&&window.ShadyCSS.flushCustomStyles()})}function n(a){!a.enqueued&&l&&(a.enqueued=!0,h(l))}m.prototype.c=function(a){a.__seenByShadyCSS||(a.__seenByShadyCSS=!0,this.customStyles.push(a),n(this))};m.prototype.b=function(a){if(a.__shadyCSSCachedStyle)return a.__shadyCSSCachedStyle;var b;a.getStyle?b=a.getStyle():b=a;return b};
-m.prototype.a=function(){for(var a=this.customStyles,b=0;b<a.length;b++){var d=a[b];if(!d.__shadyCSSCachedStyle){var e=this.b(d);e&&(e=e.__appliedElement||e,k&&k(e),d.__shadyCSSCachedStyle=e)}}return a};m.prototype.addCustomStyle=m.prototype.c;m.prototype.getStyleForCustomStyle=m.prototype.b;m.prototype.processStyles=m.prototype.a;
-Object.defineProperties(m.prototype,{transformCallback:{get:function(){return k},set:function(a){k=a}},validateCallback:{get:function(){return l},set:function(a){var b=!1;l||(b=!0);l=a;b&&n(this)}}});function p(a,b){for(var d in b)null===d?a.style.removeProperty(d):a.style.setProperty(d,b[d])};var q=!(window.ShadyDOM&&window.ShadyDOM.inUse),r;function t(a){r=a&&a.shimcssproperties?!1:q||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var u;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(u=window.ShadyCSS.cssBuild);var v=!(!window.ShadyCSS||!window.ShadyCSS.disableRuntime);
-window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?r=window.ShadyCSS.nativeCss:window.ShadyCSS?(t(window.ShadyCSS),window.ShadyCSS=void 0):t(window.WebComponents&&window.WebComponents.flags);var w=r,x=u;var y=new m;window.ShadyCSS||(window.ShadyCSS={prepareTemplate:function(){},prepareTemplateDom:function(){},prepareTemplateStyles:function(){},styleSubtree:function(a,b){y.a();p(a,b)},styleElement:function(){y.a()},styleDocument:function(a){y.a();p(document.body,a)},getComputedStyleValue:function(a,b){return(a=window.getComputedStyle(a).getPropertyValue(b))?a.trim():""},flushCustomStyles:function(){},nativeCss:w,nativeShadow:q,cssBuild:x,disableRuntime:v});window.ShadyCSS.CustomStyleInterface=y;}).call(this);
-
-//# sourceMappingURL=custom-style-interface.min.js.map
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/apply-shim.js b/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/apply-shim.js
deleted file mode 100644
index 09ba034..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/apply-shim.js
+++ /dev/null
@@ -1,223 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import ApplyShim from '../src/apply-shim.js';
-import templateMap from '../src/template-map.js';
-import {getIsExtends, toCssText, elementHasBuiltCss} from '../src/style-util.js';
-import * as ApplyShimUtils from '../src/apply-shim-utils.js';
-import {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js';
-import {CustomStyleInterfaceInterface} from '../src/custom-style-interface.js'; // eslint-disable-line no-unused-vars
-import {nativeCssVariables, nativeShadow, cssBuild, disableRuntime} from '../src/style-settings.js';
-
-/** @const {ApplyShim} */
-const applyShim = new ApplyShim();
-
-class ApplyShimInterface {
-  constructor() {
-    /** @type {?CustomStyleInterfaceInterface} */
-    this.customStyleInterface = null;
-    applyShim['invalidCallback'] = ApplyShimUtils.invalidate;
-  }
-  ensure() {
-    if (this.customStyleInterface) {
-      return;
-    }
-    if (window.ShadyCSS.CustomStyleInterface) {
-      this.customStyleInterface =
-          /** @type {!CustomStyleInterfaceInterface} */ (
-              window.ShadyCSS.CustomStyleInterface);
-      this.customStyleInterface['transformCallback'] = (style) => {
-        applyShim.transformCustomStyle(style);
-      };
-      this.customStyleInterface['validateCallback'] = () => {
-        requestAnimationFrame(() => {
-          if (this.customStyleInterface['enqueued']) {
-            this.flushCustomStyles();
-          }
-        });
-      }
-    }
-  }
-  /**
-   * @param {!HTMLTemplateElement} template
-   * @param {string} elementName
-   */
-  prepareTemplate(template, elementName) {
-    this.ensure();
-    if (elementHasBuiltCss(template)) {
-      return;
-    }
-    templateMap[elementName] = template;
-    let ast = applyShim.transformTemplate(template, elementName);
-    // save original style ast to use for revalidating instances
-    template['_styleAst'] = ast;
-  }
-  flushCustomStyles() {
-    this.ensure();
-    if (!this.customStyleInterface) {
-      return;
-    }
-    let styles = this.customStyleInterface['processStyles']();
-    if (!this.customStyleInterface['enqueued']) {
-      return;
-    }
-    for (let i = 0; i < styles.length; i++ ) {
-      let cs = styles[i];
-      let style = this.customStyleInterface['getStyleForCustomStyle'](cs);
-      if (style) {
-        applyShim.transformCustomStyle(style);
-      }
-    }
-    this.customStyleInterface['enqueued'] = false;
-  }
-  /**
-   * @param {HTMLElement} element
-   * @param {Object=} properties
-   */
-  styleSubtree(element, properties) {
-    this.ensure();
-    if (properties) {
-      updateNativeProperties(element, properties);
-    }
-    if (element.shadowRoot) {
-      this.styleElement(element);
-      let shadowChildren =
-          /** @type {!ParentNode} */ (element.shadowRoot).children ||
-          element.shadowRoot.childNodes;
-      for (let i = 0; i < shadowChildren.length; i++) {
-        this.styleSubtree(/** @type {HTMLElement} */(shadowChildren[i]));
-      }
-    } else {
-      let children = element.children || element.childNodes;
-      for (let i = 0; i < children.length; i++) {
-        this.styleSubtree(/** @type {HTMLElement} */(children[i]));
-      }
-    }
-  }
-  /**
-   * @param {HTMLElement} element
-   */
-  styleElement(element) {
-    this.ensure();
-    let {is} = getIsExtends(element);
-    let template = templateMap[is];
-    if (template && elementHasBuiltCss(template)) {
-      return;
-    }
-    if (template && !ApplyShimUtils.templateIsValid(template)) {
-      // only revalidate template once
-      if (!ApplyShimUtils.templateIsValidating(template)) {
-        this.prepareTemplate(template, is);
-        ApplyShimUtils.startValidatingTemplate(template);
-      }
-      // update this element instance
-      let root = element.shadowRoot;
-      if (root) {
-        let style = /** @type {HTMLStyleElement} */(root.querySelector('style'));
-        if (style) {
-          // reuse the template's style ast, it has all the original css text
-          style['__cssRules'] = template['_styleAst'];
-          style.textContent = toCssText(template['_styleAst'])
-        }
-      }
-    }
-  }
-  /**
-   * @param {Object=} properties
-   */
-  styleDocument(properties) {
-    this.ensure();
-    this.styleSubtree(document.body, properties);
-  }
-}
-
-if (!window.ShadyCSS || !window.ShadyCSS.ScopingShim) {
-  const applyShimInterface = new ApplyShimInterface();
-  let CustomStyleInterface = window.ShadyCSS && window.ShadyCSS.CustomStyleInterface;
-
-  /** @suppress {duplicate} */
-  window.ShadyCSS = {
-    /**
-     * @param {!HTMLTemplateElement} template
-     * @param {string} elementName
-     * @param {string=} elementExtends
-     */
-    prepareTemplate(template, elementName, elementExtends) { // eslint-disable-line no-unused-vars
-      applyShimInterface.flushCustomStyles();
-      applyShimInterface.prepareTemplate(template, elementName);
-    },
-
-    /**
-     * @param {!HTMLTemplateElement} template
-     * @param {string} elementName
-     * @param {string=} elementExtends
-     */
-    prepareTemplateStyles(template, elementName, elementExtends) {
-      window.ShadyCSS.prepareTemplate(template, elementName, elementExtends);
-    },
-
-    /**
-     * @param {!HTMLTemplateElement} template
-     * @param {string} elementName
-     */
-    prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars
-
-    /**
-     * @param {!HTMLElement} element
-     * @param {Object=} properties
-     */
-    styleSubtree(element, properties) {
-      applyShimInterface.flushCustomStyles();
-      applyShimInterface.styleSubtree(element, properties);
-    },
-
-    /**
-     * @param {!HTMLElement} element
-     */
-    styleElement(element) {
-      applyShimInterface.flushCustomStyles();
-      applyShimInterface.styleElement(element);
-    },
-
-    /**
-     * @param {Object=} properties
-     */
-    styleDocument(properties) {
-      applyShimInterface.flushCustomStyles();
-      applyShimInterface.styleDocument(properties);
-    },
-
-    /**
-     * @param {Element} element
-     * @param {string} property
-     * @return {string}
-     */
-    getComputedStyleValue(element, property) {
-      return getComputedStyleValue(element, property);
-    },
-
-    flushCustomStyles() {
-      applyShimInterface.flushCustomStyles();
-    },
-
-    nativeCss: nativeCssVariables,
-    nativeShadow: nativeShadow,
-    cssBuild: cssBuild,
-    disableRuntime: disableRuntime,
-  };
-
-  if (CustomStyleInterface) {
-    window.ShadyCSS.CustomStyleInterface = CustomStyleInterface;
-  }
-}
-
-window.ShadyCSS.ApplyShim = applyShim;
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/custom-style-interface.js b/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/custom-style-interface.js
deleted file mode 100644
index bf5024a..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/custom-style-interface.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import CustomStyleInterface from '../src/custom-style-interface.js';
-import {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js';
-import {nativeCssVariables, nativeShadow, cssBuild, disableRuntime} from '../src/style-settings.js';
-
-const customStyleInterface = new CustomStyleInterface();
-
-if (!window.ShadyCSS) {
-  window.ShadyCSS = {
-    /**
-     * @param {!HTMLTemplateElement} template
-     * @param {string} elementName
-     * @param {string=} elementExtends
-     */
-    prepareTemplate(template, elementName, elementExtends) {}, // eslint-disable-line no-unused-vars
-
-    /**
-     * @param {!HTMLTemplateElement} template
-     * @param {string} elementName
-     */
-    prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars
-
-    /**
-     * @param {!HTMLTemplateElement} template
-     * @param {string} elementName
-     * @param {string=} elementExtends
-     */
-    prepareTemplateStyles(template, elementName, elementExtends) {}, // eslint-disable-line no-unused-vars
-
-    /**
-     * @param {Element} element
-     * @param {Object=} properties
-     */
-    styleSubtree(element, properties) {
-      customStyleInterface.processStyles();
-      updateNativeProperties(element, properties);
-    },
-
-    /**
-     * @param {Element} element
-     */
-    styleElement(element) { // eslint-disable-line no-unused-vars
-      customStyleInterface.processStyles();
-    },
-
-    /**
-     * @param {Object=} properties
-     */
-    styleDocument(properties) {
-      customStyleInterface.processStyles();
-      updateNativeProperties(document.body, properties);
-    },
-
-    /**
-     * @param {Element} element
-     * @param {string} property
-     * @return {string}
-     */
-    getComputedStyleValue(element, property) {
-      return getComputedStyleValue(element, property);
-    },
-
-    flushCustomStyles() {},
-    nativeCss: nativeCssVariables,
-    nativeShadow: nativeShadow,
-    cssBuild: cssBuild,
-    disableRuntime: disableRuntime,
-  }
-}
-
-window.ShadyCSS.CustomStyleInterface = customStyleInterface;
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/scoping-shim.js b/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/scoping-shim.js
deleted file mode 100644
index b3b8982..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/scoping-shim.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import ScopingShim from '../src/scoping-shim.js';
-import {nativeCssVariables, nativeShadow, cssBuild, disableRuntime} from '../src/style-settings.js';
-
-/** @const {ScopingShim} */
-const scopingShim = new ScopingShim();
-
-let ApplyShim, CustomStyleInterface;
-
-if (window['ShadyCSS']) {
-  ApplyShim = window['ShadyCSS']['ApplyShim'];
-  CustomStyleInterface = window['ShadyCSS']['CustomStyleInterface'];
-}
-
-window.ShadyCSS = {
-  ScopingShim: scopingShim,
-  /**
-   * @param {!HTMLTemplateElement} template
-   * @param {string} elementName
-   * @param {string=} elementExtends
-   */
-  prepareTemplate(template, elementName, elementExtends) {
-    scopingShim.flushCustomStyles();
-    scopingShim.prepareTemplate(template, elementName, elementExtends)
-  },
-
-  /**
-   * @param {!HTMLTemplateElement} template
-   * @param {string} elementName
-   */
-  prepareTemplateDom(template, elementName) {
-    scopingShim.prepareTemplateDom(template, elementName);
-  },
-
-  /**
-   * @param {!HTMLTemplateElement} template
-   * @param {string} elementName
-   * @param {string=} elementExtends
-   */
-  prepareTemplateStyles(template, elementName, elementExtends) {
-    scopingShim.flushCustomStyles();
-    scopingShim.prepareTemplateStyles(template, elementName, elementExtends)
-  },
-  /**
-   * @param {!HTMLElement} element
-   * @param {Object=} properties
-   */
-  styleSubtree(element, properties) {
-    scopingShim.flushCustomStyles();
-    scopingShim.styleSubtree(element, properties);
-  },
-
-  /**
-   * @param {!HTMLElement} element
-   */
-  styleElement(element) {
-    scopingShim.flushCustomStyles();
-    scopingShim.styleElement(element);
-  },
-
-  /**
-   * @param {Object=} properties
-   */
-  styleDocument(properties) {
-    scopingShim.flushCustomStyles();
-    scopingShim.styleDocument(properties);
-  },
-
-  flushCustomStyles() {
-    scopingShim.flushCustomStyles();
-  },
-
-  /**
-   * @param {Element} element
-   * @param {string} property
-   * @return {string}
-   */
-  getComputedStyleValue(element, property) {
-    return scopingShim.getComputedStyleValue(element, property);
-  },
-
-  nativeCss: nativeCssVariables,
-
-  nativeShadow: nativeShadow,
-
-  cssBuild: cssBuild,
-
-  disableRuntime: disableRuntime,
-};
-
-if (ApplyShim) {
-  window.ShadyCSS.ApplyShim = ApplyShim;
-}
-
-if (CustomStyleInterface) {
-  window.ShadyCSS.CustomStyleInterface = CustomStyleInterface;
-}
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/externs/shadycss-externs.js b/third_party/polymer/v3_0/components-chromium/shadycss/externs/shadycss-externs.js
deleted file mode 100644
index 629a24c..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/externs/shadycss-externs.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/** @externs */
-
-/** @typedef {{
- * styleElement: function(!HTMLElement),
- * styleSubtree: function(!HTMLElement, Object<string, string>=),
- * prepareTemplate: function(!HTMLTemplateElement, string, string=),
- * prepareTemplateStyles: function(!HTMLTemplateElement, string, string=),
- * prepareTemplateDom: function(!HTMLTemplateElement, string),
- * styleDocument: function(Object<string, string>=),
- * flushCustomStyles: function(),
- * getComputedStyleValue: function(!Element, string): string,
- * ScopingShim: (Object|undefined),
- * ApplyShim: (Object|undefined),
- * CustomStyleInterface: (Object|undefined),
- * nativeCss: boolean,
- * nativeShadow: boolean,
- * cssBuild: (string | undefined),
- * disableRuntime: boolean,
- * }}
- */
-let ShadyCSSInterface; //eslint-disable-line no-unused-vars
-
-/**
- * @typedef {{
- * shimcssproperties: (boolean | undefined),
- * shimshadow: (boolean | undefined),
- * cssBuild: (string | undefined),
- * disableRuntime: (boolean | undefined),
- * }}
- */
-let ShadyCSSOptions; //eslint-disable-line no-unused-vars
-
-/** @type {(ShadyCSSInterface | ShadyCSSOptions | undefined)} */
-window.ShadyCSS;
-
-/** @type {string|undefined} */
-Element.prototype.extends;
-
-/** @type {?Element|undefined} */
-Element.prototype._element;
-
-/** @type {string|undefined} */
-Element.prototype.__cssBuild;
-
-/** @type {boolean|undefined} */
-HTMLTemplateElement.prototype._validating;
-
-/** @type {boolean|undefined} */
-HTMLTemplateElement.prototype._prepared;
-
-/** @type {boolean|undefined} */
-HTMLTemplateElement.prototype._domPrepared;
-
-/** @type {?DocumentFragment|undefined} */
-HTMLTemplateElement.prototype._content;
-
-/** @type {?HTMLStyleElement|undefined} */
-HTMLTemplateElement.prototype._gatheredStyle;
-
-/** @type {?HTMLStyleElement|undefined} */
-HTMLTemplateElement.prototype._style;
-
-/**
- * @type {string | undefined}
- */
-DOMTokenList.prototype.value;
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/scoping-shim.min.js b/third_party/polymer/v3_0/components-chromium/shadycss/scoping-shim.min.js
deleted file mode 100644
index 36d02906..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/scoping-shim.min.js
+++ /dev/null
@@ -1,59 +0,0 @@
-(function(){/*
-
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-'use strict';var k,aa="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this;function n(){this.end=this.start=0;this.rules=this.parent=this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""}
-function p(a){a=a.replace(ba,"").replace(ca,"");var b=da,c=a,e=new n;e.start=0;e.end=c.length;for(var d=e,f=0,g=c.length;f<g;f++)if("{"===c[f]){d.rules||(d.rules=[]);var h=d,l=h.rules[h.rules.length-1]||null;d=new n;d.start=f+1;d.parent=h;d.previous=l;h.rules.push(d)}else"}"===c[f]&&(d.end=f+1,d=d.parent||e);return b(e,a)}
-function da(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=ea(c),c=c.replace(fa," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=ha:c.match(ia)&&(a.type=q,a.keyframesName=a.selector.split(fa).pop()):a.type=0===c.indexOf("--")?ja:ka);if(c=a.rules)for(var e=0,d=c.length,f=void 0;e<d&&(f=c[e]);e++)da(f,
-b);return a}function ea(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})}
-function la(a,b,c){c=void 0===c?"":c;var e="";if(a.cssText||a.rules){var d=a.rules,f;if(f=d)f=d[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var g=d.length,h=void 0;f<g&&(h=d[f]);f++)e=la(h,b,e)}else b?b=a.cssText:(b=a.cssText,b=b.replace(ma,"").replace(na,""),b=b.replace(oa,"").replace(pa,"")),(e=b.trim())&&(e="  "+e+"\n")}e&&(a.selector&&(c+=a.selector+" {\n"),c+=e,a.selector&&(c+="}\n\n"));return c}
-var ka=1,q=7,ha=4,ja=1E3,ba=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,ca=/@import[^;]*;/gim,ma=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,na=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,oa=/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,pa=/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,ia=/^@[^\s]*keyframes/,fa=/\s+/g;var r=!(window.ShadyDOM&&window.ShadyDOM.inUse),t;function qa(a){t=a&&a.shimcssproperties?!1:r||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var ra;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(ra=window.ShadyCSS.cssBuild);var u=!(!window.ShadyCSS||!window.ShadyCSS.disableRuntime);
-window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?t=window.ShadyCSS.nativeCss:window.ShadyCSS?(qa(window.ShadyCSS),window.ShadyCSS=void 0):qa(window.WebComponents&&window.WebComponents.flags);var v=t,w=ra;var x=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi,y=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,sa=/(--[\w-]+)\s*([:,;)]|$)/gi,ta=/(animation\s*:)|(animation-name\s*:)/,ua=/@media\s(.*)/,va=/\{[^}]*\}/g;var wa=new Set;function z(a,b){if(!a)return"";"string"===typeof a&&(a=p(a));b&&A(a,b);return la(a,v)}function B(a){!a.__cssRules&&a.textContent&&(a.__cssRules=p(a.textContent));return a.__cssRules||null}function xa(a){return!!a.parent&&a.parent.type===q}function A(a,b,c,e){if(a){var d=!1,f=a.type;if(e&&f===ha){var g=a.selector.match(ua);g&&(window.matchMedia(g[1]).matches||(d=!0))}f===ka?b(a):c&&f===q?c(a):f===ja&&(d=!0);if((a=a.rules)&&!d)for(d=0,f=a.length,g=void 0;d<f&&(g=a[d]);d++)A(g,b,c,e)}}
-function C(a,b,c,e){var d=document.createElement("style");b&&d.setAttribute("scope",b);d.textContent=a;ya(d,c,e);return d}var D=null;function za(a){a=document.createComment(" Shady DOM styles for "+a+" ");var b=document.head;b.insertBefore(a,(D?D.nextSibling:null)||b.firstChild);return D=a}function ya(a,b,c){b=b||document.head;b.insertBefore(a,c&&c.nextSibling||b.firstChild);D?a.compareDocumentPosition(D)===Node.DOCUMENT_POSITION_PRECEDING&&(D=a):D=a}
-function Aa(a,b){for(var c=0,e=a.length;b<e;b++)if("("===a[b])c++;else if(")"===a[b]&&0===--c)return b;return-1}function Ba(a,b){var c=a.indexOf("var(");if(-1===c)return b(a,"","","");var e=Aa(a,c+3),d=a.substring(c+4,e);c=a.substring(0,c);a=Ba(a.substring(e+1),b);e=d.indexOf(",");return-1===e?b(c,d.trim(),"",a):b(c,d.substring(0,e).trim(),d.substring(e+1).trim(),a)}function E(a,b){r?a.setAttribute("class",b):window.ShadyDOM.nativeMethods.setAttribute.call(a,"class",b)}
-var F=window.ShadyDOM&&window.ShadyDOM.wrap||function(a){return a};function G(a){var b=a.localName,c="";b?-1<b.indexOf("-")||(c=b,b=a.getAttribute&&a.getAttribute("is")||""):(b=a.is,c=a.extends);return{is:b,s:c}}function Ca(a){for(var b=[],c="",e=0;0<=e&&e<a.length;e++)if("("===a[e]){var d=Aa(a,e);c+=a.slice(e,d+1);e=d}else","===a[e]?(b.push(c),c=""):c+=a[e];c&&b.push(c);return b}
-function H(a){if(void 0!==w)return w;if(void 0===a.__cssBuild){var b=a.getAttribute("css-build");if(b)a.__cssBuild=b;else{a:{b="template"===a.localName?a.content.firstChild:a.firstChild;if(b instanceof Comment&&(b=b.textContent.trim().split(":"),"css-build"===b[0])){b=b[1];break a}b=""}if(""!==b){var c="template"===a.localName?a.content.firstChild:a.firstChild;c.parentNode.removeChild(c)}a.__cssBuild=b}}return a.__cssBuild||""}
-function Da(a){a=void 0===a?"":a;return""!==a&&v?r?"shadow"===a:"shady"===a:!1};function I(){}function Ea(a,b){J(K,a,function(a){L(a,b||"")})}function J(a,b,c){b.nodeType===Node.ELEMENT_NODE&&c(b);var e;"template"===b.localName?e=(b.content||b._content||b).childNodes:e=b.children||b.childNodes;if(e)for(b=0;b<e.length;b++)J(a,e[b],c)}
-function L(a,b,c){if(b)if(a.classList)c?(a.classList.remove("style-scope"),a.classList.remove(b)):(a.classList.add("style-scope"),a.classList.add(b));else if(a.getAttribute){var e=a.getAttribute("class");c?e&&(b=e.replace("style-scope","").replace(b,""),E(a,b)):E(a,(e?e+" ":"")+"style-scope "+b)}}function Fa(a,b,c){J(K,a,function(a){L(a,b,!0);L(a,c)})}function Ga(a,b){J(K,a,function(a){L(a,b||"",!0)})}
-function M(a,b,c,e,d){var f=K;d=void 0===d?"":d;""===d&&(r||"shady"===(void 0===e?"":e)?d=z(b,c):(a=G(a),d=Ha(f,b,a.is,a.s,c)+"\n\n"));return d.trim()}function Ha(a,b,c,e,d){var f=Ia(c,e);c=c?"."+c:"";return z(b,function(b){b.c||(b.selector=b.g=Ja(a,b,a.b,c,f),b.c=!0);d&&d(b,c,f)})}function Ia(a,b){return b?"[is="+a+"]":a}function Ja(a,b,c,e,d){var f=Ca(b.selector);if(!xa(b)){b=0;for(var g=f.length,h=void 0;b<g&&(h=f[b]);b++)f[b]=c.call(a,h,e,d)}return f.filter(function(a){return!!a}).join(",")}
-function Ka(a){return a.replace(La,function(a,c,e){-1<e.indexOf("+")?e=e.replace(/\+/g,"___"):-1<e.indexOf("___")&&(e=e.replace(/___/g,"+"));return":"+c+"("+e+")"})}function Ma(a){for(var b=[],c;c=a.match(Na);){var e=c.index,d=Aa(a,e);if(-1===d)throw Error(c.input+" selector missing ')'");c=a.slice(e,d+1);a=a.replace(c,"\ue000");b.push(c)}return{A:a,matches:b}}function Oa(a,b){var c=a.split("\ue000");return b.reduce(function(a,b,f){return a+b+c[f+1]},c[0])}
-I.prototype.b=function(a,b,c){var e=!1;a=a.trim();var d=La.test(a);d&&(a=a.replace(La,function(a,b,c){return":"+b+"("+c.replace(/\s/g,"")+")"}),a=Ka(a));var f=Na.test(a);if(f){var g=Ma(a);a=g.A;g=g.matches}a=a.replace(Pa,":host $1");a=a.replace(Qa,function(a,d,f){e||(a=Ra(f,d,b,c),e=e||a.stop,d=a.G,f=a.value);return d+f});f&&(a=Oa(a,g));d&&(a=Ka(a));return a=a.replace(Sa,function(a,b,c,d){return'[dir="'+c+'"] '+b+d+", "+b+'[dir="'+c+'"]'+d})};
-function Ra(a,b,c,e){var d=a.indexOf("::slotted");0<=a.indexOf(":host")?a=Ta(a,e):0!==d&&(a=c?Ua(a,c):a);c=!1;0<=d&&(b="",c=!0);if(c){var f=!0;c&&(a=a.replace(Va,function(a,b){return" > "+b}))}return{value:a,G:b,stop:f}}function Ua(a,b){a=a.split(/(\[.+?\])/);for(var c=[],e=0;e<a.length;e++)if(1===e%2)c.push(a[e]);else{var d=a[e];if(""!==d||e!==a.length-1)d=d.split(":"),d[0]+=b,c.push(d.join(":"))}return c.join("")}
-function Ta(a,b){var c=a.match(Wa);return(c=c&&c[2].trim()||"")?c[0].match(Xa)?a.replace(Wa,function(a,c,f){return b+f}):c.split(Xa)[0]===b?c:"should_not_match":a.replace(":host",b)}function Ya(a){":root"===a.selector&&(a.selector="html")}I.prototype.c=function(a){return a.match(":host")?"":a.match("::slotted")?this.b(a,":not(.style-scope)"):Ua(a.trim(),":not(.style-scope)")};aa.Object.defineProperties(I.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"style-scope"}}});
-var La=/:(nth[-\w]+)\(([^)]+)\)/,Qa=/(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g,Xa=/[[.:#*]/,Pa=/^(::slotted)/,Wa=/(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Va=/(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Sa=/(.*):dir\((?:(ltr|rtl))\)(.*)/,Na=/:(?:matches|any|-(?:webkit|moz)-any)/,K=new I;function N(a,b,c,e,d){this.o=a||null;this.b=b||null;this.w=c||[];this.i=null;this.cssBuild=d||"";this.s=e||"";this.a=this.j=this.m=null}function O(a){return a?a.__styleInfo:null}function Za(a,b){return a.__styleInfo=b}N.prototype.c=function(){return this.o};N.prototype._getStyleRules=N.prototype.c;function $a(a){var b=this.matches||this.matchesSelector||this.mozMatchesSelector||this.msMatchesSelector||this.oMatchesSelector||this.webkitMatchesSelector;return b&&b.call(this,a)}var ab=navigator.userAgent.match("Trident");function bb(){}function cb(a){var b={},c=[],e=0;A(a,function(a){P(a);a.index=e++;a=a.f.cssText;for(var c;c=sa.exec(a);){var d=c[1];":"!==c[2]&&(b[d]=!0)}},function(a){c.push(a)});a.b=c;a=[];for(var d in b)a.push(d);return a}
-function P(a){if(!a.f){var b={},c={};R(a,c)&&(b.l=c,a.rules=null);b.cssText=a.parsedCssText.replace(va,"").replace(x,"");a.f=b}}function R(a,b){var c=a.f;if(c){if(c.l)return Object.assign(b,c.l),!0}else{c=a.parsedCssText;for(var e;a=x.exec(c);){e=(a[2]||a[3]).trim();if("inherit"!==e||"unset"!==e)b[a[1].trim()]=e;e=!0}return e}}
-function S(a,b,c){b&&(b=0<=b.indexOf(";")?db(a,b,c):Ba(b,function(b,d,f,g){if(!d)return b+g;(d=S(a,c[d],c))&&"initial"!==d?"apply-shim-inherit"===d&&(d="inherit"):d=S(a,c[f]||f,c)||f;return b+(d||"")+g}));return b&&b.trim()||""}
-function db(a,b,c){b=b.split(";");for(var e=0,d,f;e<b.length;e++)if(d=b[e]){y.lastIndex=0;if(f=y.exec(d))d=S(a,c[f[1]],c);else if(f=d.indexOf(":"),-1!==f){var g=d.substring(f);g=g.trim();g=S(a,g,c)||g;d=d.substring(0,f)+g}b[e]=d&&d.lastIndexOf(";")===d.length-1?d.slice(0,-1):d||""}return b.join(";")}
-function eb(a,b){var c={},e=[];A(a,function(a){a.f||P(a);var d=a.g||a.parsedSelector;b&&a.f.l&&d&&$a.call(b,d)&&(R(a,c),a=a.index,d=parseInt(a/32,10),e[d]=(e[d]||0)|1<<a%32)},null,!0);return{l:c,key:e}}
-function fb(a,b,c,e){b.f||P(b);if(b.f.l){var d=G(a);a=d.is;d=d.s;d=a?Ia(a,d):"html";var f=b.parsedSelector,g=":host > *"===f||"html"===f,h=0===f.indexOf(":host")&&!g;"shady"===c&&(g=f===d+" > *."+d||-1!==f.indexOf("html"),h=!g&&0===f.indexOf(d));if(g||h)c=d,h&&(b.g||(b.g=Ja(K,b,K.b,a?"."+a:"",d)),c=b.g||d),e({A:c,K:h,T:g})}}function gb(a,b,c){var e={},d={};A(b,function(b){fb(a,b,c,function(c){$a.call(a._element||a,c.A)&&(c.K?R(b,e):R(b,d))})},null,!0);return{M:d,J:e}}
-function hb(a,b,c,e){var d=G(b),f=Ia(d.is,d.s),g=new RegExp("(?:^|[^.#[:])"+(b.extends?"\\"+f.slice(0,-1)+"\\]":f)+"($|[.:[\\s>+~])"),h=O(b);d=h.o;h=h.cssBuild;var l=ib(d,e);return M(b,d,function(b){var d="";b.f||P(b);b.f.cssText&&(d=db(a,b.f.cssText,c));b.cssText=d;if(!r&&!xa(b)&&b.cssText){var h=d=b.cssText;null==b.C&&(b.C=ta.test(d));if(b.C)if(null==b.u){b.u=[];for(var m in l)h=l[m],h=h(d),d!==h&&(d=h,b.u.push(m))}else{for(m=0;m<b.u.length;++m)h=l[b.u[m]],d=h(d);h=d}b.cssText=h;b.g=b.g||b.selector;
-d="."+e;m=Ca(b.g);h=0;for(var Ab=m.length,Q=void 0;h<Ab&&(Q=m[h]);h++)m[h]=Q.match(g)?Q.replace(f,d):d+" "+Q;b.selector=m.join(",")}},h)}function ib(a,b){a=a.b;var c={};if(!r&&a)for(var e=0,d=a[e];e<a.length;d=a[++e]){var f=d,g=b;f.h=new RegExp("\\b"+f.keyframesName+"(?!\\B|-)","g");f.a=f.keyframesName+"-"+g;f.g=f.g||f.selector;f.selector=f.g.replace(f.keyframesName,f.a);c[d.keyframesName]=jb(d)}return c}function jb(a){return function(b){return b.replace(a.h,a.a)}}
-function kb(a,b){var c=T,e=B(a);a.textContent=z(e,function(a){var d=a.cssText=a.parsedCssText;a.f&&a.f.cssText&&(d=d.replace(ma,"").replace(na,""),a.cssText=db(c,d,b))})}aa.Object.defineProperties(bb.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"x-scope"}}});var T=new bb;var U={},V=window.customElements;if(V&&!r&&!u){var lb=V.define;V.define=function(a,b,c){U[a]||(U[a]=za(a));lb.call(V,a,b,c)}};function mb(){this.cache={}}mb.prototype.store=function(a,b,c,e){var d=this.cache[a]||[];d.push({l:b,styleElement:c,j:e});100<d.length&&d.shift();this.cache[a]=d};function nb(){}var ob=new RegExp(K.a+"\\s*([^\\s]*)");function pb(a){return(a=(a.classList&&a.classList.value?a.classList.value:a.getAttribute("class")||"").match(ob))?a[1]:""}function qb(a){var b=F(a).getRootNode();return b===a||b===a.ownerDocument?"":(a=b.host)?G(a).is:""}
-function rb(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.target!==document.documentElement&&c.target!==document.head)for(var e=0;e<c.addedNodes.length;e++){var d=c.addedNodes[e];if(d.nodeType===Node.ELEMENT_NODE){var f=d.getRootNode(),g=pb(d);if(g&&f===d.ownerDocument&&("style"!==d.localName&&"template"!==d.localName||""===H(d)))Ga(d,g);else if(f instanceof ShadowRoot)for(f=qb(d),f!==g&&Fa(d,g,f),d=window.ShadyDOM.nativeMethods.querySelectorAll.call(d,":not(."+K.a+")"),g=0;g<d.length;g++){f=d[g];
-var h=qb(f);h&&L(f,h)}}}}}
-if(!(r||window.ShadyDOM&&window.ShadyDOM.handlesDynamicScoping)){var sb=new MutationObserver(rb),tb=function(a){sb.observe(a,{childList:!0,subtree:!0})};if(window.customElements&&!window.customElements.polyfillWrapFlushCallback)tb(document);else{var ub=function(){tb(document.body)};window.HTMLImports?window.HTMLImports.whenReady(ub):requestAnimationFrame(function(){if("loading"===document.readyState){var a=function(){ub();document.removeEventListener("readystatechange",a)};document.addEventListener("readystatechange",
-a)}else ub()})}nb=function(){rb(sb.takeRecords())}}var vb=nb;var W={};var wb=Promise.resolve();function xb(a){if(a=W[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function yb(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function zb(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a._validating||(a._validating=!0,wb.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a._validating=!1}))};var Bb={},Cb=new mb;function X(){this.B={};this.c=document.documentElement;var a=new n;a.rules=[];this.h=Za(this.c,new N(a));this.v=!1;this.b=this.a=null}k=X.prototype;k.flush=function(){vb()};k.I=function(a){return B(a)};k.R=function(a){return z(a)};k.prepareTemplate=function(a,b,c){this.prepareTemplateDom(a,b);this.prepareTemplateStyles(a,b,c)};
-k.prepareTemplateStyles=function(a,b,c){if(!a._prepared&&!u){r||U[b]||(U[b]=za(b));a._prepared=!0;a.name=b;a.extends=c;W[b]=a;var e=H(a),d=Da(e);c={is:b,extends:c};for(var f=[],g=a.content.querySelectorAll("style"),h=0;h<g.length;h++){var l=g[h];if(l.hasAttribute("shady-unscoped")){if(!r){var m=l.textContent;wa.has(m)||(wa.add(m),m=l.cloneNode(!0),document.head.appendChild(m));l.parentNode.removeChild(l)}}else f.push(l.textContent),l.parentNode.removeChild(l)}f=f.join("").trim()+(Bb[b]||"");Y(this);
-if(!d){if(g=!e)g=y.test(f)||x.test(f),y.lastIndex=0,x.lastIndex=0;h=p(f);g&&v&&this.a&&this.a.transformRules(h,b);a._styleAst=h}g=[];v||(g=cb(a._styleAst));if(!g.length||v)h=r?a.content:null,b=U[b]||null,e=M(c,a._styleAst,null,e,d?f:""),e=e.length?C(e,c.is,h,b):null,a._style=e;a.a=g}};k.L=function(a,b){Bb[b]=a.join(" ")};k.prepareTemplateDom=function(a,b){if(!u){var c=H(a);r||"shady"===c||a._domPrepared||(a._domPrepared=!0,Ea(a.content,b))}};
-function Db(a){var b=G(a),c=b.is;b=b.s;var e=U[c]||null,d=W[c];if(d){c=d._styleAst;var f=d.a;d=H(d);b=new N(c,e,f,b,d);Za(a,b);return b}}function Eb(a){!a.b&&window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface&&(a.b=window.ShadyCSS.CustomStyleInterface,a.b.transformCallback=function(b){a.D(b)},a.b.validateCallback=function(){requestAnimationFrame(function(){(a.b.enqueued||a.v)&&a.flushCustomStyles()})})}
-function Y(a){!a.a&&window.ShadyCSS&&window.ShadyCSS.ApplyShim&&(a.a=window.ShadyCSS.ApplyShim,a.a.invalidCallback=xb);Eb(a)}
-k.flushCustomStyles=function(){if(!u&&(Y(this),this.b)){var a=this.b.processStyles();if(this.b.enqueued&&!Da(this.h.cssBuild)){if(v){if(!this.h.cssBuild)for(var b=0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);if(c&&v&&this.a){var e=B(c);Y(this);this.a.transformRules(e);c.textContent=z(e)}}}else{Fb(this,this.c,this.h);for(b=0;b<a.length;b++)(c=this.b.getStyleForCustomStyle(a[b]))&&kb(c,this.h.m);this.v&&this.styleDocument()}this.b.enqueued=!1}}};
-k.styleElement=function(a,b){if(u){if(b){O(a)||Za(a,new N(null));var c=O(a);c.i=c.i||{};Object.assign(c.i,b);Gb(this,a,c)}}else if(c=O(a)||Db(a))if(a!==this.c&&(this.v=!0),b&&(c.i=c.i||{},Object.assign(c.i,b)),v)Gb(this,a,c);else if(this.flush(),Fb(this,a,c),c.w&&c.w.length){b=G(a).is;var e;a:{if(e=Cb.cache[b])for(var d=e.length-1;0<=d;d--){var f=e[d];b:{var g=c.w;for(var h=0;h<g.length;h++){var l=g[h];if(f.l[l]!==c.m[l]){g=!1;break b}}g=!0}if(g){e=f;break a}}e=void 0}g=e?e.styleElement:null;d=c.j;
-(f=e&&e.j)||(f=this.B[b]=(this.B[b]||0)+1,f=b+"-"+f);c.j=f;f=c.j;h=T;h=g?g.textContent||"":hb(h,a,c.m,f);l=O(a);var m=l.a;m&&!r&&m!==g&&(m._useCount--,0>=m._useCount&&m.parentNode&&m.parentNode.removeChild(m));r?l.a?(l.a.textContent=h,g=l.a):h&&(g=C(h,f,a.shadowRoot,l.b)):g?g.parentNode||(ab&&-1<h.indexOf("@media")&&(g.textContent=h),ya(g,null,l.b)):h&&(g=C(h,f,null,l.b));g&&(g._useCount=g._useCount||0,l.a!=g&&g._useCount++,l.a=g);f=g;r||(g=c.j,l=h=a.getAttribute("class")||"",d&&(l=h.replace(new RegExp("\\s*x-scope\\s*"+
-d+"\\s*","g")," ")),l+=(l?" ":"")+"x-scope "+g,h!==l&&E(a,l));e||Cb.store(b,c.m,f,c.j)}};
-function Gb(a,b,c){var e=G(b).is;if(c.i){var d=c.i,f;for(f in d)null===f?b.style.removeProperty(f):b.style.setProperty(f,d[f])}d=W[e];if(!(!d&&b!==a.c||d&&""!==H(d))&&d&&d._style&&!yb(d)){if(yb(d)||d._applyShimValidatingVersion!==d._applyShimNextVersion)Y(a),a.a&&a.a.transformRules(d._styleAst,e),d._style.textContent=M(b,c.o),zb(d);r&&(a=b.shadowRoot)&&(a=a.querySelector("style"))&&(a.textContent=M(b,c.o));c.o=d._styleAst}}
-function Hb(a,b){return(b=F(b).getRootNode().host)?O(b)||Db(b)?b:Hb(a,b):a.c}function Fb(a,b,c){var e=Hb(a,b),d=O(e),f=d.m;e===a.c||f||(Fb(a,e,d),f=d.m);a=Object.create(f||null);e=gb(b,c.o,c.cssBuild);b=eb(d.o,b).l;Object.assign(a,e.J,b,e.M);b=c.i;for(var g in b)if((d=b[g])||0===d)a[g]=d;g=T;b=Object.getOwnPropertyNames(a);for(d=0;d<b.length;d++)e=b[d],a[e]=S(g,a[e],a);c.m=a}k.styleDocument=function(a){this.styleSubtree(this.c,a)};
-k.styleSubtree=function(a,b){var c=F(a),e=c.shadowRoot;(e||a===this.c)&&this.styleElement(a,b);if(a=e&&(e.children||e.childNodes))for(c=0;c<a.length;c++)this.styleSubtree(a[c]);else if(c=c.children||c.childNodes)for(a=0;a<c.length;a++)this.styleSubtree(c[a])};
-k.D=function(a){var b=this,c=H(a);c!==this.h.cssBuild&&(this.h.cssBuild=c);if(!Da(c)){var e=B(a);A(e,function(a){if(r)Ya(a);else{var d=K;a.selector=a.parsedSelector;Ya(a);a.selector=a.g=Ja(d,a,d.c,void 0,void 0)}v&&""===c&&(Y(b),b.a&&b.a.transformRule(a))});v?a.textContent=z(e):this.h.o.rules.push(e)}};k.getComputedStyleValue=function(a,b){var c;v||(c=(O(a)||O(Hb(this,a))).m[b]);return(c=c||window.getComputedStyle(a).getPropertyValue(b))?c.trim():""};
-k.P=function(a,b){var c=F(a).getRootNode();b=b?b.split(/\s/):[];c=c.host&&c.host.localName;if(!c){var e=a.getAttribute("class");if(e){e=e.split(/\s/);for(var d=0;d<e.length;d++)if(e[d]===K.a){c=e[d+1];break}}}c&&b.push(K.a,c);v||(c=O(a))&&c.j&&b.push(T.a,c.j);E(a,b.join(" "))};k.F=function(a){return O(a)};k.O=function(a,b){L(a,b)};k.S=function(a,b){L(a,b,!0)};k.N=function(a){return qb(a)};k.H=function(a){return pb(a)};X.prototype.flush=X.prototype.flush;X.prototype.prepareTemplate=X.prototype.prepareTemplate;
-X.prototype.styleElement=X.prototype.styleElement;X.prototype.styleDocument=X.prototype.styleDocument;X.prototype.styleSubtree=X.prototype.styleSubtree;X.prototype.getComputedStyleValue=X.prototype.getComputedStyleValue;X.prototype.setElementClass=X.prototype.P;X.prototype._styleInfoForNode=X.prototype.F;X.prototype.transformCustomStyleForDocument=X.prototype.D;X.prototype.getStyleAst=X.prototype.I;X.prototype.styleAstToString=X.prototype.R;X.prototype.flushCustomStyles=X.prototype.flushCustomStyles;
-X.prototype.scopeNode=X.prototype.O;X.prototype.unscopeNode=X.prototype.S;X.prototype.scopeForNode=X.prototype.N;X.prototype.currentScopeForNode=X.prototype.H;X.prototype.prepareAdoptedCssText=X.prototype.L;Object.defineProperties(X.prototype,{nativeShadow:{get:function(){return r}},nativeCss:{get:function(){return v}}});var Z=new X,Ib,Jb;window.ShadyCSS&&(Ib=window.ShadyCSS.ApplyShim,Jb=window.ShadyCSS.CustomStyleInterface);
-window.ShadyCSS={ScopingShim:Z,prepareTemplate:function(a,b,c){Z.flushCustomStyles();Z.prepareTemplate(a,b,c)},prepareTemplateDom:function(a,b){Z.prepareTemplateDom(a,b)},prepareTemplateStyles:function(a,b,c){Z.flushCustomStyles();Z.prepareTemplateStyles(a,b,c)},styleSubtree:function(a,b){Z.flushCustomStyles();Z.styleSubtree(a,b)},styleElement:function(a){Z.flushCustomStyles();Z.styleElement(a)},styleDocument:function(a){Z.flushCustomStyles();Z.styleDocument(a)},flushCustomStyles:function(){Z.flushCustomStyles()},
-getComputedStyleValue:function(a,b){return Z.getComputedStyleValue(a,b)},nativeCss:v,nativeShadow:r,cssBuild:w,disableRuntime:u};Ib&&(window.ShadyCSS.ApplyShim=Ib);Jb&&(window.ShadyCSS.CustomStyleInterface=Jb);}).call(this);
-
-//# sourceMappingURL=scoping-shim.min.js.map
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim-utils.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim-utils.js
deleted file mode 100644
index c5c27a2..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim-utils.js
+++ /dev/null
@@ -1,149 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-import templateMap from './template-map.js';
-import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
-
-/*
- * Utilities for handling invalidating apply-shim mixins for a given template.
- *
- * The invalidation strategy involves keeping track of the "current" version of a template's mixins, and updating that count when a mixin is invalidated.
- * The template
- */
-
-/** @const {string} */
-const CURRENT_VERSION = '_applyShimCurrentVersion';
-
-/** @const {string} */
-const NEXT_VERSION = '_applyShimNextVersion';
-
-/** @const {string} */
-const VALIDATING_VERSION = '_applyShimValidatingVersion';
-
-/**
- * @const {Promise<void>}
- */
-const promise = Promise.resolve();
-
-/**
- * @param {string} elementName
- */
-export function invalidate(elementName){
-  let template = templateMap[elementName];
-  if (template) {
-    invalidateTemplate(template);
-  }
-}
-
-/**
- * This function can be called multiple times to mark a template invalid
- * and signal that the style inside must be regenerated.
- *
- * Use `startValidatingTemplate` to begin an asynchronous validation cycle.
- * During that cycle, call `templateIsValidating` to see if the template must
- * be revalidated
- * @param {HTMLTemplateElement} template
- */
-export function invalidateTemplate(template) {
-  // default the current version to 0
-  template[CURRENT_VERSION] = template[CURRENT_VERSION] || 0;
-  // ensure the "validating for" flag exists
-  template[VALIDATING_VERSION] = template[VALIDATING_VERSION] || 0;
-  // increment the next version
-  template[NEXT_VERSION] = (template[NEXT_VERSION] || 0) + 1;
-}
-
-/**
- * @param {string} elementName
- * @return {boolean}
- */
-export function isValid(elementName) {
-  let template = templateMap[elementName];
-  if (template) {
-    return templateIsValid(template);
-  }
-  return true;
-}
-
-/**
- * @param {HTMLTemplateElement} template
- * @return {boolean}
- */
-export function templateIsValid(template) {
-  return template[CURRENT_VERSION] === template[NEXT_VERSION];
-}
-
-/**
- * @param {string} elementName
- * @return {boolean}
- */
-export function isValidating(elementName) {
-  let template = templateMap[elementName];
-  if (template) {
-    return templateIsValidating(template);
-  }
-  return false;
-}
-
-/**
- * Returns true if the template is currently invalid and `startValidating` has been called since the last invalidation.
- * If false, the template must be validated.
- * @param {HTMLTemplateElement} template
- * @return {boolean}
- */
-export function templateIsValidating(template) {
-  return !templateIsValid(template) && template[VALIDATING_VERSION] === template[NEXT_VERSION];
-}
-
-/**
- * the template is marked as `validating` for one microtask so that all instances
- * found in the tree crawl of `applyStyle` will update themselves,
- * but the template will only be updated once.
- * @param {string} elementName
-*/
-export function startValidating(elementName) {
-  let template = templateMap[elementName];
-  startValidatingTemplate(template);
-}
-
-/**
- * Begin an asynchronous invalidation cycle.
- * This should be called after every validation of a template
- *
- * After one microtask, the template will be marked as valid until the next call to `invalidateTemplate`
- * @param {HTMLTemplateElement} template
- */
-export function startValidatingTemplate(template) {
-  // remember that the current "next version" is the reason for this validation cycle
-  template[VALIDATING_VERSION] = template[NEXT_VERSION];
-  // however, there only needs to be one async task to clear the counters
-  if (!template._validating) {
-    template._validating = true;
-    promise.then(function() {
-      // sync the current version to let future invalidations cause a refresh cycle
-      template[CURRENT_VERSION] = template[NEXT_VERSION];
-      template._validating = false;
-    });
-  }
-}
-
-/**
- * @return {boolean}
- */
-export function elementsAreInvalid() {
-  for (let elementName in templateMap) {
-    let template = templateMap[elementName];
-    if (!templateIsValid(template)) {
-      return true;
-    }
-  }
-  return false;
-}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim.js
deleted file mode 100644
index e4bc9cd..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim.js
+++ /dev/null
@@ -1,525 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-/*
- * The apply shim simulates the behavior of `@apply` proposed at
- * https://tabatkins.github.io/specs/css-apply-rule/.
- * The approach is to convert a property like this:
- *
- *    --foo: {color: red; background: blue;}
- *
- * to this:
- *
- *    --foo_-_color: red;
- *    --foo_-_background: blue;
- *
- * Then where `@apply --foo` is used, that is converted to:
- *
- *    color: var(--foo_-_color);
- *    background: var(--foo_-_background);
- *
- * This approach generally works but there are some issues and limitations.
- * Consider, for example, that somewhere *between* where `--foo` is set and used,
- * another element sets it to:
- *
- *    --foo: { border: 2px solid red; }
- *
- * We must now ensure that the color and background from the previous setting
- * do not apply. This is accomplished by changing the property set to this:
- *
- *    --foo_-_border: 2px solid red;
- *    --foo_-_color: initial;
- *    --foo_-_background: initial;
- *
- * This works but introduces one new issue.
- * Consider this setup at the point where the `@apply` is used:
- *
- *    background: orange;
- *    `@apply` --foo;
- *
- * In this case the background will be unset (initial) rather than the desired
- * `orange`. We address this by altering the property set to use a fallback
- * value like this:
- *
- *    color: var(--foo_-_color);
- *    background: var(--foo_-_background, orange);
- *    border: var(--foo_-_border);
- *
- * Note that the default is retained in the property set and the `background` is
- * the desired `orange`. This leads us to a limitation.
- *
- * Limitation 1:
-
- * Only properties in the rule where the `@apply`
- * is used are considered as default values.
- * If another rule matches the element and sets `background` with
- * less specificity than the rule in which `@apply` appears,
- * the `background` will not be set.
- *
- * Limitation 2:
- *
- * When using Polymer's `updateStyles` api, new properties may not be set for
- * `@apply` properties.
-
-*/
-
-'use strict';
-
-import {forEachRule, processVariableAndFallback, rulesForStyle, toCssText, gatherStyleText} from './style-util.js';
-import {MIXIN_MATCH, VAR_ASSIGN} from './common-regex.js';
-import {detectMixin} from './common-utils.js';
-import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
-
-const APPLY_NAME_CLEAN = /;\s*/m;
-const INITIAL_INHERIT = /^\s*(initial)|(inherit)\s*$/;
-const IMPORTANT = /\s*!important/;
-
-// separator used between mixin-name and mixin-property-name when producing properties
-// NOTE: plain '-' may cause collisions in user styles
-const MIXIN_VAR_SEP = '_-_';
-
-/**
- * @typedef {!Object<string, string>}
- */
-let PropertyEntry; // eslint-disable-line no-unused-vars
-
-/**
- * @typedef {!Object<string, boolean>}
- */
-let DependantsEntry; // eslint-disable-line no-unused-vars
-
-/** @typedef {{
- *    properties: PropertyEntry,
- *    dependants: DependantsEntry
- * }}
- */
-let MixinMapEntry; // eslint-disable-line no-unused-vars
-
-// map of mixin to property names
-// --foo: {border: 2px} -> {properties: {(--foo, ['border'])}, dependants: {'element-name': proto}}
-class MixinMap {
-  constructor() {
-    /** @type {!Object<string, !MixinMapEntry>} */
-    this._map = {};
-  }
-  /**
-   * @param {string} name
-   * @param {!PropertyEntry} props
-   */
-  set(name, props) {
-    name = name.trim();
-    this._map[name] = {
-      properties: props,
-      dependants: {}
-    }
-  }
-  /**
-   * @param {string} name
-   * @return {MixinMapEntry}
-   */
-  get(name) {
-    name = name.trim();
-    return this._map[name] || null;
-  }
-}
-
-/**
- * Callback for when an element is marked invalid
- * @type {?function(string)}
- */
-let invalidCallback = null;
-
-/** @unrestricted */
-class ApplyShim {
-  constructor() {
-    /** @type {?string} */
-    this._currentElement = null;
-    /** @type {HTMLMetaElement} */
-    this._measureElement = null;
-    this._map = new MixinMap();
-  }
-  /**
-   * return true if `cssText` contains a mixin definition or consumption
-   * @param {string} cssText
-   * @return {boolean}
-   */
-  detectMixin(cssText) {
-    return detectMixin(cssText);
-  }
-
-  /**
-   * Gather styles into one style for easier processing
-   * @param {!HTMLTemplateElement} template
-   * @return {HTMLStyleElement}
-   */
-  gatherStyles(template) {
-    const styleText = gatherStyleText(template.content);
-    if (styleText) {
-      const style = /** @type {!HTMLStyleElement} */(document.createElement('style'));
-      style.textContent = styleText;
-      template.content.insertBefore(style, template.content.firstChild);
-      return style;
-    }
-    return null;
-  }
-  /**
-   * @param {!HTMLTemplateElement} template
-   * @param {string} elementName
-   * @return {StyleNode}
-   */
-  transformTemplate(template, elementName) {
-    if (template._gatheredStyle === undefined) {
-      template._gatheredStyle = this.gatherStyles(template);
-    }
-    /** @type {HTMLStyleElement} */
-    const style = template._gatheredStyle;
-    return style ? this.transformStyle(style, elementName) : null;
-  }
-  /**
-   * @param {!HTMLStyleElement} style
-   * @param {string} elementName
-   * @return {StyleNode}
-   */
-  transformStyle(style, elementName = '') {
-    let ast = rulesForStyle(style);
-    this.transformRules(ast, elementName);
-    style.textContent = toCssText(ast);
-    return ast;
-  }
-  /**
-   * @param {!HTMLStyleElement} style
-   * @return {StyleNode}
-   */
-  transformCustomStyle(style) {
-    let ast = rulesForStyle(style);
-    forEachRule(ast, (rule) => {
-      if (rule['selector'] === ':root') {
-        rule['selector'] = 'html';
-      }
-      this.transformRule(rule);
-    })
-    style.textContent = toCssText(ast);
-    return ast;
-  }
-  /**
-   * @param {StyleNode} rules
-   * @param {string} elementName
-   */
-  transformRules(rules, elementName) {
-    this._currentElement = elementName;
-    forEachRule(rules, (r) => {
-      this.transformRule(r);
-    });
-    this._currentElement = null;
-  }
-  /**
-   * @param {!StyleNode} rule
-   */
-  transformRule(rule) {
-    rule['cssText'] = this.transformCssText(rule['parsedCssText'], rule);
-    // :root was only used for variable assignment in property shim,
-    // but generates invalid selectors with real properties.
-    // replace with `:host > *`, which serves the same effect
-    if (rule['selector'] === ':root') {
-      rule['selector'] = ':host > *';
-    }
-  }
-  /**
-   * @param {string} cssText
-   * @param {!StyleNode} rule
-   * @return {string}
-   */
-  transformCssText(cssText, rule) {
-    // produce variables
-    cssText = cssText.replace(VAR_ASSIGN, (matchText, propertyName, valueProperty, valueMixin) =>
-      this._produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule));
-    // consume mixins
-    return this._consumeCssProperties(cssText, rule);
-  }
-  /**
-   * @param {string} property
-   * @return {string}
-   */
-  _getInitialValueForProperty(property) {
-    if (!this._measureElement) {
-      this._measureElement = /** @type {HTMLMetaElement} */(document.createElement('meta'));
-      this._measureElement.setAttribute('apply-shim-measure', '');
-      this._measureElement.style.all = 'initial';
-      document.head.appendChild(this._measureElement);
-    }
-    return window.getComputedStyle(this._measureElement).getPropertyValue(property);
-  }
-  /**
-   * Walk over all rules before this rule to find fallbacks for mixins
-   *
-   * @param {!StyleNode} startRule
-   * @return {!Object}
-   */
-  _fallbacksFromPreviousRules(startRule) {
-    // find the "top" rule
-    let topRule = startRule;
-    while (topRule['parent']) {
-      topRule = topRule['parent'];
-    }
-    const fallbacks = {};
-    let seenStartRule = false;
-    forEachRule(topRule, (r) => {
-      // stop when we hit the input rule
-      seenStartRule = seenStartRule || r === startRule;
-      if (seenStartRule) {
-        return;
-      }
-      // NOTE: Only matching selectors are "safe" for this fallback processing
-      // It would be prohibitive to run `matchesSelector()` on each selector,
-      // so we cheat and only check if the same selector string is used, which
-      // guarantees things like specificity matching
-      if (r['selector'] === startRule['selector']) {
-        Object.assign(fallbacks, this._cssTextToMap(r['parsedCssText']));
-      }
-    });
-    return fallbacks;
-  }
-  /**
-   * replace mixin consumption with variable consumption
-   * @param {string} text
-   * @param {!StyleNode=} rule
-   * @return {string}
-   */
-  _consumeCssProperties(text, rule) {
-    /** @type {Array} */
-    let m = null;
-    // loop over text until all mixins with defintions have been applied
-    while((m = MIXIN_MATCH.exec(text))) {
-      let matchText = m[0];
-      let mixinName = m[1];
-      let idx = m.index;
-      // collect properties before apply to be "defaults" if mixin might override them
-      // match includes a "prefix", so find the start and end positions of @apply
-      let applyPos = idx + matchText.indexOf('@apply');
-      let afterApplyPos = idx + matchText.length;
-      // find props defined before this @apply
-      let textBeforeApply = text.slice(0, applyPos);
-      let textAfterApply = text.slice(afterApplyPos);
-      let defaults = rule ? this._fallbacksFromPreviousRules(rule) : {};
-      Object.assign(defaults, this._cssTextToMap(textBeforeApply));
-      let replacement = this._atApplyToCssProperties(mixinName, defaults);
-      // use regex match position to replace mixin, keep linear processing time
-      text = `${textBeforeApply}${replacement}${textAfterApply}`;
-      // move regex search to _after_ replacement
-      MIXIN_MATCH.lastIndex = idx + replacement.length;
-    }
-    return text;
-  }
-  /**
-   * produce variable consumption at the site of mixin consumption
-   * `@apply` --foo; -> for all props (${propname}: var(--foo_-_${propname}, ${fallback[propname]}}))
-   * Example:
-   *  border: var(--foo_-_border); padding: var(--foo_-_padding, 2px)
-   *
-   * @param {string} mixinName
-   * @param {Object} fallbacks
-   * @return {string}
-   */
-  _atApplyToCssProperties(mixinName, fallbacks) {
-    mixinName = mixinName.replace(APPLY_NAME_CLEAN, '');
-    let vars = [];
-    let mixinEntry = this._map.get(mixinName);
-    // if we depend on a mixin before it is created
-    // make a sentinel entry in the map to add this element as a dependency for when it is defined.
-    if (!mixinEntry) {
-      this._map.set(mixinName, {});
-      mixinEntry = this._map.get(mixinName);
-    }
-    if (mixinEntry) {
-      if (this._currentElement) {
-        mixinEntry.dependants[this._currentElement] = true;
-      }
-      let p, parts, f;
-      const properties = mixinEntry.properties;
-      for (p in properties) {
-        f = fallbacks && fallbacks[p];
-        parts = [p, ': var(', mixinName, MIXIN_VAR_SEP, p];
-        if (f) {
-          parts.push(',', f.replace(IMPORTANT, ''));
-        }
-        parts.push(')');
-        if (IMPORTANT.test(properties[p])) {
-          parts.push(' !important');
-        }
-        vars.push(parts.join(''));
-      }
-    }
-    return vars.join('; ');
-  }
-
-  /**
-   * @param {string} property
-   * @param {string} value
-   * @return {string}
-   */
-  _replaceInitialOrInherit(property, value) {
-    let match = INITIAL_INHERIT.exec(value);
-    if (match) {
-      if (match[1]) {
-        // initial
-        // replace `initial` with the concrete initial value for this property
-        value = this._getInitialValueForProperty(property);
-      } else {
-        // inherit
-        // with this purposfully illegal value, the variable will be invalid at
-        // compute time (https://www.w3.org/TR/css-variables/#invalid-at-computed-value-time)
-        // and for inheriting values, will behave similarly
-        // we cannot support the same behavior for non inheriting values like 'border'
-        value = 'apply-shim-inherit';
-      }
-    }
-    return value;
-  }
-
-  /**
-   * "parse" a mixin definition into a map of properties and values
-   * cssTextToMap('border: 2px solid black') -> ('border', '2px solid black')
-   * @param {string} text
-   * @param {boolean=} replaceInitialOrInherit
-   * @return {!Object<string, string>}
-   */
-  _cssTextToMap(text, replaceInitialOrInherit = false) {
-    let props = text.split(';');
-    let property, value;
-    let out = {};
-    for (let i = 0, p, sp; i < props.length; i++) {
-      p = props[i];
-      if (p) {
-        sp = p.split(':');
-        // ignore lines that aren't definitions like @media
-        if (sp.length > 1) {
-          property = sp[0].trim();
-          // some properties may have ':' in the value, like data urls
-          value = sp.slice(1).join(':');
-          if (replaceInitialOrInherit) {
-            value = this._replaceInitialOrInherit(property, value);
-          }
-          out[property] = value;
-        }
-      }
-    }
-    return out;
-  }
-
-  /**
-   * @param {MixinMapEntry} mixinEntry
-   */
-  _invalidateMixinEntry(mixinEntry) {
-    if (!invalidCallback) {
-      return;
-    }
-    for (let elementName in mixinEntry.dependants) {
-      if (elementName !== this._currentElement) {
-        invalidCallback(elementName);
-      }
-    }
-  }
-
-  /**
-   * @param {string} matchText
-   * @param {string} propertyName
-   * @param {?string} valueProperty
-   * @param {?string} valueMixin
-   * @param {!StyleNode} rule
-   * @return {string}
-   */
-  _produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule) {
-    // handle case where property value is a mixin
-    if (valueProperty) {
-      // form: --mixin2: var(--mixin1), where --mixin1 is in the map
-      processVariableAndFallback(valueProperty, (prefix, value) => {
-        if (value && this._map.get(value)) {
-          valueMixin = `@apply ${value};`
-        }
-      });
-    }
-    if (!valueMixin) {
-      return matchText;
-    }
-    let mixinAsProperties = this._consumeCssProperties('' + valueMixin, rule);
-    let prefix = matchText.slice(0, matchText.indexOf('--'));
-    // `initial` and `inherit` as properties in a map should be replaced because
-    // these keywords are eagerly evaluated when the mixin becomes CSS Custom Properties,
-    // and would set the variable value, rather than carry the keyword to the `var()` usage.
-    let mixinValues = this._cssTextToMap(mixinAsProperties, true);
-    let combinedProps = mixinValues;
-    let mixinEntry = this._map.get(propertyName);
-    let oldProps = mixinEntry && mixinEntry.properties;
-    if (oldProps) {
-      // NOTE: since we use mixin, the map of properties is updated here
-      // and this is what we want.
-      combinedProps = Object.assign(Object.create(oldProps), mixinValues);
-    } else {
-      this._map.set(propertyName, combinedProps);
-    }
-    let out = [];
-    let p, v;
-    // set variables defined by current mixin
-    let needToInvalidate = false;
-    for (p in combinedProps) {
-      v = mixinValues[p];
-      // if property not defined by current mixin, set initial
-      if (v === undefined) {
-        v = 'initial';
-      }
-      if (oldProps && !(p in oldProps)) {
-        needToInvalidate = true;
-      }
-      out.push(`${propertyName}${MIXIN_VAR_SEP}${p}: ${v}`);
-    }
-    if (needToInvalidate) {
-      this._invalidateMixinEntry(mixinEntry);
-    }
-    if (mixinEntry) {
-      mixinEntry.properties = combinedProps;
-    }
-    // because the mixinMap is global, the mixin might conflict with
-    // a different scope's simple variable definition:
-    // Example:
-    // some style somewhere:
-    // --mixin1:{ ... }
-    // --mixin2: var(--mixin1);
-    // some other element:
-    // --mixin1: 10px solid red;
-    // --foo: var(--mixin1);
-    // In this case, we leave the original variable definition in place.
-    if (valueProperty) {
-      prefix = `${matchText};${prefix}`;
-    }
-    return `${prefix}${out.join('; ')};`;
-  }
-}
-
-/* exports */
-/* eslint-disable no-self-assign */
-ApplyShim.prototype['detectMixin'] = ApplyShim.prototype.detectMixin;
-ApplyShim.prototype['transformStyle'] = ApplyShim.prototype.transformStyle;
-ApplyShim.prototype['transformCustomStyle'] = ApplyShim.prototype.transformCustomStyle;
-ApplyShim.prototype['transformRules'] = ApplyShim.prototype.transformRules;
-ApplyShim.prototype['transformRule'] = ApplyShim.prototype.transformRule;
-ApplyShim.prototype['transformTemplate'] = ApplyShim.prototype.transformTemplate;
-ApplyShim.prototype['_separator'] = MIXIN_VAR_SEP;
-/* eslint-enable no-self-assign */
-Object.defineProperty(ApplyShim.prototype, 'invalidCallback', {
-  /** @return {?function(string)} */
-  get() {
-    return invalidCallback;
-  },
-  /** @param {?function(string)} cb */
-  set(cb) {
-    invalidCallback = cb;
-  }
-});
-
-export default ApplyShim;
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/common-regex.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/common-regex.js
deleted file mode 100644
index 126fc15c..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/common-regex.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-export const VAR_ASSIGN = /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi;
-export const MIXIN_MATCH = /(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi;
-export const VAR_CONSUMED = /(--[\w-]+)\s*([:,;)]|$)/gi;
-export const ANIMATION_MATCH = /(animation\s*:)|(animation-name\s*:)/;
-export const MEDIA_MATCH = /@media\s(.*)/;
-export const IS_VAR = /^--/;
-export const BRACKETED = /\{[^}]*\}/g;
-export const HOST_PREFIX = '(?:^|[^.#[:])';
-export const HOST_SUFFIX = '($|[.:[\\s>+~])';
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/common-utils.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/common-utils.js
deleted file mode 100644
index 863250b..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/common-utils.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import { MIXIN_MATCH, VAR_ASSIGN } from './common-regex.js';
-
-/**
- * @param {Element} element
- * @param {Object=} properties
- */
-export function updateNativeProperties(element, properties) {
-  // remove previous properties
-  for (let p in properties) {
-    // NOTE: for bc with shim, don't apply null values.
-    if (p === null) {
-      element.style.removeProperty(p);
-    } else {
-      element.style.setProperty(p, properties[p]);
-    }
-  }
-}
-
-/**
- * @param {Element} element
- * @param {string} property
- * @return {string}
- */
-export function getComputedStyleValue(element, property) {
-  /**
-   * @const {string}
-   */
-  const value = window.getComputedStyle(element).getPropertyValue(property);
-  if (!value) {
-    return '';
-  } else {
-    return value.trim();
-  }
-}
-
-/**
- * return true if `cssText` contains a mixin definition or consumption
- * @param {string} cssText
- * @return {boolean}
- */
-export function detectMixin(cssText) {
-  const has = MIXIN_MATCH.test(cssText) || VAR_ASSIGN.test(cssText);
-  // reset state of the regexes
-  MIXIN_MATCH.lastIndex = 0;
-  VAR_ASSIGN.lastIndex = 0;
-  return has;
-}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/css-parse.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/css-parse.js
deleted file mode 100644
index 8a8fb1c..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/css-parse.js
+++ /dev/null
@@ -1,264 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-/*
-Extremely simple css parser. Intended to be not more than what we need
-and definitely not necessarily correct =).
-*/
-
-'use strict';
-
-/** @unrestricted */
-class StyleNode {
-  constructor() {
-    /** @type {number} */
-    this['start'] = 0;
-    /** @type {number} */
-    this['end'] = 0;
-    /** @type {StyleNode} */
-    this['previous'] = null;
-    /** @type {StyleNode} */
-    this['parent'] = null;
-    /** @type {Array<StyleNode>} */
-    this['rules'] = null;
-    /** @type {string} */
-    this['parsedCssText'] = '';
-    /** @type {string} */
-    this['cssText'] = '';
-    /** @type {boolean} */
-    this['atRule'] = false;
-    /** @type {number} */
-    this['type'] = 0;
-    /** @type {string} */
-    this['keyframesName'] = '';
-    /** @type {string} */
-    this['selector'] = '';
-    /** @type {string} */
-    this['parsedSelector'] = '';
-  }
-}
-
-export {StyleNode}
-
-// given a string of css, return a simple rule tree
-/**
- * @param {string} text
- * @return {StyleNode}
- */
-export function parse(text) {
-  text = clean(text);
-  return parseCss(lex(text), text);
-}
-
-// remove stuff we don't care about that may hinder parsing
-/**
- * @param {string} cssText
- * @return {string}
- */
-function clean(cssText) {
-  return cssText.replace(RX.comments, '').replace(RX.port, '');
-}
-
-// super simple {...} lexer that returns a node tree
-/**
- * @param {string} text
- * @return {StyleNode}
- */
-function lex(text) {
-  let root = new StyleNode();
-  root['start'] = 0;
-  root['end'] = text.length
-  let n = root;
-  for (let i = 0, l = text.length; i < l; i++) {
-    if (text[i] === OPEN_BRACE) {
-      if (!n['rules']) {
-        n['rules'] = [];
-      }
-      let p = n;
-      let previous = p['rules'][p['rules'].length - 1] || null;
-      n = new StyleNode();
-      n['start'] = i + 1;
-      n['parent'] = p;
-      n['previous'] = previous;
-      p['rules'].push(n);
-    } else if (text[i] === CLOSE_BRACE) {
-      n['end'] = i + 1;
-      n = n['parent'] || root;
-    }
-  }
-  return root;
-}
-
-// add selectors/cssText to node tree
-/**
- * @param {StyleNode} node
- * @param {string} text
- * @return {StyleNode}
- */
-function parseCss(node, text) {
-  let t = text.substring(node['start'], node['end'] - 1);
-  node['parsedCssText'] = node['cssText'] = t.trim();
-  if (node['parent']) {
-    let ss = node['previous'] ? node['previous']['end'] : node['parent']['start'];
-    t = text.substring(ss, node['start'] - 1);
-    t = _expandUnicodeEscapes(t);
-    t = t.replace(RX.multipleSpaces, ' ');
-    // TODO(sorvell): ad hoc; make selector include only after last ;
-    // helps with mixin syntax
-    t = t.substring(t.lastIndexOf(';') + 1);
-    let s = node['parsedSelector'] = node['selector'] = t.trim();
-    node['atRule'] = (s.indexOf(AT_START) === 0);
-    // note, support a subset of rule types...
-    if (node['atRule']) {
-      if (s.indexOf(MEDIA_START) === 0) {
-        node['type'] = types.MEDIA_RULE;
-      } else if (s.match(RX.keyframesRule)) {
-        node['type'] = types.KEYFRAMES_RULE;
-        node['keyframesName'] =
-          node['selector'].split(RX.multipleSpaces).pop();
-      }
-    } else {
-      if (s.indexOf(VAR_START) === 0) {
-        node['type'] = types.MIXIN_RULE;
-      } else {
-        node['type'] = types.STYLE_RULE;
-      }
-    }
-  }
-  let r$ = node['rules'];
-  if (r$) {
-    for (let i = 0, l = r$.length, r;
-      (i < l) && (r = r$[i]); i++) {
-      parseCss(r, text);
-    }
-  }
-  return node;
-}
-
-/**
- * conversion of sort unicode escapes with spaces like `\33 ` (and longer) into
- * expanded form that doesn't require trailing space `\000033`
- * @param {string} s
- * @return {string}
- */
-function _expandUnicodeEscapes(s) {
-  return s.replace(/\\([0-9a-f]{1,6})\s/gi, function() {
-    let code = arguments[1],
-      repeat = 6 - code.length;
-    while (repeat--) {
-      code = '0' + code;
-    }
-    return '\\' + code;
-  });
-}
-
-/**
- * stringify parsed css.
- * @param {StyleNode} node
- * @param {boolean=} preserveProperties
- * @param {string=} text
- * @return {string}
- */
-export function stringify(node, preserveProperties, text = '') {
-  // calc rule cssText
-  let cssText = '';
-  if (node['cssText'] || node['rules']) {
-    let r$ = node['rules'];
-    if (r$ && !_hasMixinRules(r$)) {
-      for (let i = 0, l = r$.length, r;
-        (i < l) && (r = r$[i]); i++) {
-        cssText = stringify(r, preserveProperties, cssText);
-      }
-    } else {
-      cssText = preserveProperties ? node['cssText'] :
-        removeCustomProps(node['cssText']);
-      cssText = cssText.trim();
-      if (cssText) {
-        cssText = '  ' + cssText + '\n';
-      }
-    }
-  }
-  // emit rule if there is cssText
-  if (cssText) {
-    if (node['selector']) {
-      text += node['selector'] + ' ' + OPEN_BRACE + '\n';
-    }
-    text += cssText;
-    if (node['selector']) {
-      text += CLOSE_BRACE + '\n\n';
-    }
-  }
-  return text;
-}
-
-/**
- * @param {Array<StyleNode>} rules
- * @return {boolean}
- */
-function _hasMixinRules(rules) {
-  let r = rules[0];
-  return Boolean(r) && Boolean(r['selector']) && r['selector'].indexOf(VAR_START) === 0;
-}
-
-/**
- * @param {string} cssText
- * @return {string}
- */
-function removeCustomProps(cssText) {
-  cssText = removeCustomPropAssignment(cssText);
-  return removeCustomPropApply(cssText);
-}
-
-/**
- * @param {string} cssText
- * @return {string}
- */
-export function removeCustomPropAssignment(cssText) {
-  return cssText
-    .replace(RX.customProp, '')
-    .replace(RX.mixinProp, '');
-}
-
-/**
- * @param {string} cssText
- * @return {string}
- */
-function removeCustomPropApply(cssText) {
-  return cssText
-    .replace(RX.mixinApply, '')
-    .replace(RX.varApply, '');
-}
-
-/** @enum {number} */
-export const types = {
-  STYLE_RULE: 1,
-  KEYFRAMES_RULE: 7,
-  MEDIA_RULE: 4,
-  MIXIN_RULE: 1000
-}
-
-const OPEN_BRACE = '{';
-const CLOSE_BRACE = '}';
-
-// helper regexp's
-const RX = {
-  comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
-  port: /@import[^;]*;/gim,
-  customProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,
-  mixinProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,
-  mixinApply: /@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,
-  varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,
-  keyframesRule: /^@[^\s]*keyframes/,
-  multipleSpaces: /\s+/g
-}
-
-const VAR_START = '--';
-const MEDIA_START = '@media';
-const AT_START = '@';
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/custom-style-interface.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/custom-style-interface.js
deleted file mode 100644
index 257433b..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/custom-style-interface.js
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import documentWait from './document-wait.js';
-
-/**
- * @typedef {HTMLStyleElement | {getStyle: function():HTMLStyleElement}}
- */
-export let CustomStyleProvider;
-
-const SEEN_MARKER = '__seenByShadyCSS';
-const CACHED_STYLE = '__shadyCSSCachedStyle';
-
-/** @type {?function(!HTMLStyleElement)} */
-let transformFn = null;
-
-/** @type {?function()} */
-let validateFn = null;
-
-/**
-This interface is provided to add document-level <style> elements to ShadyCSS for processing.
-These styles must be processed by ShadyCSS to simulate ShadowRoot upper-bound encapsulation from outside styles
-In addition, these styles may also need to be processed for @apply rules and CSS Custom Properties
-
-To add document-level styles to ShadyCSS, one can call `ShadyCSS.addDocumentStyle(styleElement)` or `ShadyCSS.addDocumentStyle({getStyle: () => styleElement})`
-
-In addition, if the process used to discover document-level styles can be synchronously flushed, one should set `ShadyCSS.documentStyleFlush`.
-This function will be called when calculating styles.
-
-An example usage of the document-level styling api can be found in `examples/document-style-lib.js`
-
-@unrestricted
-*/
-export default class CustomStyleInterface {
-  constructor() {
-    /** @type {!Array<!CustomStyleProvider>} */
-    this['customStyles'] = [];
-    this['enqueued'] = false;
-    // NOTE(dfreedm): use quotes here to prevent closure inlining to `function(){}`;
-    documentWait(() => {
-      if (window['ShadyCSS']['flushCustomStyles']) {
-        window['ShadyCSS']['flushCustomStyles']();
-      }
-    })
-  }
-  /**
-   * Queue a validation for new custom styles to batch style recalculations
-   */
-  enqueueDocumentValidation() {
-    if (this['enqueued'] || !validateFn) {
-      return;
-    }
-    this['enqueued'] = true;
-    documentWait(validateFn);
-  }
-  /**
-   * @param {!HTMLStyleElement} style
-   */
-  addCustomStyle(style) {
-    if (!style[SEEN_MARKER]) {
-      style[SEEN_MARKER] = true;
-      this['customStyles'].push(style);
-      this.enqueueDocumentValidation();
-    }
-  }
-  /**
-   * @param {!CustomStyleProvider} customStyle
-   * @return {HTMLStyleElement}
-   */
-  getStyleForCustomStyle(customStyle) {
-    if (customStyle[CACHED_STYLE]) {
-      return customStyle[CACHED_STYLE];
-    }
-    let style;
-    if (customStyle['getStyle']) {
-      style = customStyle['getStyle']();
-    } else {
-      style = customStyle;
-    }
-    return style;
-  }
-  /**
-   * @return {!Array<!CustomStyleProvider>}
-   */
-  processStyles() {
-    const cs = this['customStyles'];
-    for (let i = 0; i < cs.length; i++) {
-      const customStyle = cs[i];
-      if (customStyle[CACHED_STYLE]) {
-        continue;
-      }
-      const style = this.getStyleForCustomStyle(customStyle);
-      if (style) {
-        // HTMLImports polyfill may have cloned the style into the main document,
-        // which is referenced with __appliedElement.
-        const styleToTransform = /** @type {!HTMLStyleElement} */(style['__appliedElement'] || style);
-        if (transformFn) {
-          transformFn(styleToTransform);
-        }
-        customStyle[CACHED_STYLE] = styleToTransform;
-      }
-    }
-    return cs;
-  }
-}
-
-/* eslint-disable no-self-assign */
-CustomStyleInterface.prototype['addCustomStyle'] = CustomStyleInterface.prototype.addCustomStyle;
-CustomStyleInterface.prototype['getStyleForCustomStyle'] = CustomStyleInterface.prototype.getStyleForCustomStyle;
-CustomStyleInterface.prototype['processStyles'] = CustomStyleInterface.prototype.processStyles;
-/* eslint-enable no-self-assign */
-
-Object.defineProperties(CustomStyleInterface.prototype, {
-  'transformCallback': {
-    /** @return {?function(!HTMLStyleElement)} */
-    get() {
-      return transformFn;
-    },
-    /** @param {?function(!HTMLStyleElement)} fn */
-    set(fn) {
-      transformFn = fn;
-    }
-  },
-  'validateCallback': {
-    /** @return {?function()} */
-    get() {
-      return validateFn;
-    },
-    /**
-     * @param {?function()} fn
-     * @this {CustomStyleInterface}
-     */
-    set(fn) {
-      let needsEnqueue = false;
-      if (!validateFn) {
-        needsEnqueue = true;
-      }
-      validateFn = fn;
-      if (needsEnqueue) {
-        this.enqueueDocumentValidation();
-      }
-    },
-  }
-})
-
-/** @typedef {{
- * customStyles: !Array<!CustomStyleProvider>,
- * addCustomStyle: function(!CustomStyleProvider),
- * getStyleForCustomStyle: function(!CustomStyleProvider): HTMLStyleElement,
- * findStyles: function(),
- * transformCallback: ?function(!HTMLStyleElement),
- * validateCallback: ?function()
- * }}
- */
-export const CustomStyleInterfaceInterface = {};
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/document-wait.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/document-wait.js
deleted file mode 100644
index 398ca05..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/document-wait.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-/** @type {Promise<void>} */
-let readyPromise = null;
-
-/** @type {?function(?function())} */
-let whenReady = window['HTMLImports'] && window['HTMLImports']['whenReady'] || null;
-
-/** @type {function()} */
-let resolveFn;
-
-/**
- * @param {?function()} callback
- */
-export default function documentWait(callback) {
-  requestAnimationFrame(function() {
-    if (whenReady) {
-      whenReady(callback)
-    } else {
-      if (!readyPromise) {
-        readyPromise = new Promise((resolve) => {resolveFn = resolve});
-        if (document.readyState === 'complete') {
-          resolveFn();
-        } else {
-          document.addEventListener('readystatechange', () => {
-            if (document.readyState === 'complete') {
-              resolveFn();
-            }
-          });
-        }
-      }
-      readyPromise.then(function(){ callback && callback(); });
-    }
-  });
-}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/document-watcher.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/document-watcher.js
deleted file mode 100644
index 9cf34f05..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/document-watcher.js
+++ /dev/null
@@ -1,198 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import {nativeShadow} from './style-settings.js';
-import StyleTransformer from './style-transformer.js';
-import {getIsExtends, elementHasBuiltCss, wrap} from './style-util.js';
-
-export let flush = function() {};
-
-/**
- * @param {!Element} element
- * @return {string}
- */
-function getClasses(element) {
-  if (element.classList && element.classList.value) {
-    return element.classList.value;
-  } else {
-    // NOTE: className is patched to remove scoping classes in ShadyDOM
-    // use getAttribute('class') instead, which is unpatched
-    return element.getAttribute('class') || '';
-  }
-}
-
-const scopeRegExp = new RegExp(`${StyleTransformer.SCOPE_NAME}\\s*([^\\s]*)`);
-
-/**
- * @param {!Element} element
- * @return {string}
- */
-export function getCurrentScope(element) {
-  const match = getClasses(element).match(scopeRegExp);
-  if (match) {
-    return match[1];
-  } else {
-    return '';
-  }
-}
-
-/**
- * @param {!Node} node
- */
-export function getOwnerScope(node) {
-  const ownerRoot = wrap(node).getRootNode();
-  if (ownerRoot === node || ownerRoot === node.ownerDocument) {
-    return '';
-  }
-  const host = /** @type {!ShadowRoot} */(ownerRoot).host;
-  if (!host) {
-    // this may actually be a document fragment
-    return '';
-  }
-  return getIsExtends(host).is;
-}
-
-/**
- * @param {!Element} element
- */
-export function ensureCorrectScope(element) {
-  const currentScope = getCurrentScope(element);
-  const ownerRoot = wrap(element).getRootNode();
-  if (ownerRoot === element) {
-    return;
-  }
-  if (currentScope && ownerRoot === element.ownerDocument) {
-    // node was scoped, but now is in document
-    StyleTransformer.domRemoveScope(element, currentScope);
-  } else if (ownerRoot instanceof ShadowRoot) {
-    const ownerScope = getOwnerScope(element);
-    if (ownerScope !== currentScope) {
-      // node was scoped, but not by its current owner
-      StyleTransformer.domReplaceScope(element, currentScope, ownerScope);
-    }
-  }
-}
-
-/**
- * @param {!HTMLElement|!HTMLDocument} element
- */
-export function ensureCorrectSubtreeScoping(element) {
-  // find unscoped subtree nodes
-  const unscopedNodes = window['ShadyDOM']['nativeMethods']['querySelectorAll'].call(
-    element, `:not(.${StyleTransformer.SCOPE_NAME})`);
-
-  for (let j = 0; j < unscopedNodes.length; j++) {
-    // it's possible, during large batch inserts, that nodes that aren't
-    // scoped within the current scope were added.
-    // To make sure that any unscoped nodes that were inserted in the current batch are correctly styled,
-    // query all unscoped nodes and force their style-scope to be applied.
-    // This could happen if a sub-element appended an unscoped node in its shadowroot and this function
-    // runs on a parent element of the host of that unscoped node:
-    // parent-element -> element -> unscoped node
-    // Here unscoped node should have the style-scope element, not parent-element.
-    const unscopedNode = unscopedNodes[j];
-    const scopeForPreviouslyUnscopedNode = getOwnerScope(unscopedNode);
-    if (scopeForPreviouslyUnscopedNode) {
-      StyleTransformer.element(unscopedNode, scopeForPreviouslyUnscopedNode);
-    }
-  }
-}
-
-/**
- * @param {HTMLElement} el
- * @return {boolean}
- */
-function isElementWithBuiltCss(el) {
-  if (el.localName === 'style' || el.localName === 'template') {
-    return elementHasBuiltCss(el);
-  }
-  return false;
-}
-
-/**
- * @param {Array<MutationRecord|null>|null} mxns
- */
-function handler(mxns) {
-  for (let x=0; x < mxns.length; x++) {
-    let mxn = mxns[x];
-    if (mxn.target === document.documentElement ||
-      mxn.target === document.head) {
-      continue;
-    }
-    for (let i=0; i < mxn.addedNodes.length; i++) {
-      let n = mxn.addedNodes[i];
-      if (n.nodeType !== Node.ELEMENT_NODE) {
-        continue;
-      }
-      n = /** @type {HTMLElement} */(n); // eslint-disable-line no-self-assign
-      let root = n.getRootNode();
-      let currentScope = getCurrentScope(n);
-      // node was scoped, but now is in document
-      // If this element has built css, we must not remove scoping as this node
-      // will be used as a template or style without re - applying scoping as an optimization
-      if (currentScope && root === n.ownerDocument && !isElementWithBuiltCss(n)) {
-        StyleTransformer.domRemoveScope(n, currentScope);
-      } else if (root instanceof ShadowRoot) {
-        const newScope = getOwnerScope(n);
-        // rescope current node and subtree if necessary
-        if (newScope !== currentScope) {
-          StyleTransformer.domReplaceScope(n, currentScope, newScope);
-        }
-        // make sure all the subtree elements are scoped correctly
-        ensureCorrectSubtreeScoping(n);
-      }
-    }
-  }
-}
-
-// if native Shadow DOM is being used, or ShadyDOM handles dynamic scoiping, do not activate the MutationObserver
-if (!nativeShadow && !(window['ShadyDOM'] && window['ShadyDOM']['handlesDynamicScoping'])) {
-  let observer = new MutationObserver(handler);
-  let start = (node) => {
-    observer.observe(node, {childList: true, subtree: true});
-  }
-  let nativeCustomElements = (window['customElements'] &&
-    !window['customElements']['polyfillWrapFlushCallback']);
-  // need to start immediately with native custom elements
-  // TODO(dfreedm): with polyfilled HTMLImports and native custom elements
-  // excessive mutations may be observed; this can be optimized via cooperation
-  // with the HTMLImports polyfill.
-  if (nativeCustomElements) {
-    start(document);
-  } else {
-    let delayedStart = () => {
-      start(document.body);
-    }
-    // use polyfill timing if it's available
-    if (window['HTMLImports']) {
-      window['HTMLImports']['whenReady'](delayedStart);
-    // otherwise push beyond native imports being ready
-    // which requires RAF + readystate interactive.
-    } else {
-      requestAnimationFrame(function() {
-        if (document.readyState === 'loading') {
-          let listener = function() {
-            delayedStart();
-            document.removeEventListener('readystatechange', listener);
-          }
-          document.addEventListener('readystatechange', listener);
-        } else {
-          delayedStart();
-        }
-      });
-    }
-  }
-
-  flush = function() {
-    handler(observer.takeRecords());
-  }
-}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/scoping-shim.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/scoping-shim.js
deleted file mode 100644
index 4deeece..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/scoping-shim.js
+++ /dev/null
@@ -1,607 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import {parse, StyleNode} from './css-parse.js';
-import {nativeShadow, nativeCssVariables, disableRuntime} from './style-settings.js';
-import StyleTransformer from './style-transformer.js';
-import * as StyleUtil from './style-util.js';
-import StyleProperties from './style-properties.js';
-import {ensureStylePlaceholder, getStylePlaceholder} from './style-placeholder.js';
-import StyleInfo from './style-info.js';
-import StyleCache from './style-cache.js';
-import {flush as watcherFlush, getOwnerScope, getCurrentScope} from './document-watcher.js';
-import templateMap from './template-map.js';
-import * as ApplyShimUtils from './apply-shim-utils.js';
-import {updateNativeProperties, detectMixin} from './common-utils.js';
-import {CustomStyleInterfaceInterface} from './custom-style-interface.js'; // eslint-disable-line no-unused-vars
-
-/** @type {!Object<string, string>} */
-const adoptedCssTextMap = {};
-
-/**
- * @const {StyleCache}
- */
-const styleCache = new StyleCache();
-
-export default class ScopingShim {
-  constructor() {
-    this._scopeCounter = {};
-    this._documentOwner = /** @type {!HTMLElement} */(document.documentElement);
-    let ast = new StyleNode();
-    ast['rules'] = [];
-    this._documentOwnerStyleInfo = StyleInfo.set(this._documentOwner, new StyleInfo(ast));
-    this._elementsHaveApplied = false;
-    /** @type {?Object} */
-    this._applyShim = null;
-    /** @type {?CustomStyleInterfaceInterface} */
-    this._customStyleInterface = null;
-  }
-  flush() {
-    watcherFlush();
-  }
-  _generateScopeSelector(name) {
-    let id = this._scopeCounter[name] = (this._scopeCounter[name] || 0) + 1;
-    return `${name}-${id}`;
-  }
-  getStyleAst(style) {
-    return StyleUtil.rulesForStyle(style);
-  }
-  styleAstToString(ast) {
-    return StyleUtil.toCssText(ast);
-  }
-  _gatherStyles(template) {
-    return StyleUtil.gatherStyleText(template.content);
-  }
-  /**
-   * Prepare the styling and template for the given element type
-   *
-   * @param {!HTMLTemplateElement} template
-   * @param {string} elementName
-   * @param {string=} typeExtension
-   */
-  prepareTemplate(template, elementName, typeExtension) {
-    this.prepareTemplateDom(template, elementName);
-    this.prepareTemplateStyles(template, elementName, typeExtension);
-  }
-  /**
-   * Prepare styling for the given element type
-   * @param {!HTMLTemplateElement} template
-   * @param {string} elementName
-   * @param {string=} typeExtension
-   */
-  prepareTemplateStyles(template, elementName, typeExtension) {
-    if (template._prepared || disableRuntime) {
-      return;
-    }
-    // style placeholders are only used when ShadyDOM is active
-    if (!nativeShadow) {
-      ensureStylePlaceholder(elementName);
-    }
-    template._prepared = true;
-    template.name = elementName;
-    template.extends = typeExtension;
-    templateMap[elementName] = template;
-    let cssBuild = StyleUtil.getCssBuild(template);
-    const optimalBuild = StyleUtil.isOptimalCssBuild(cssBuild);
-    let info = {
-      is: elementName,
-      extends: typeExtension,
-    };
-    let cssText = this._gatherStyles(template) + (adoptedCssTextMap[elementName] || '');
-    // check if the styling has mixin definitions or uses
-    this._ensure();
-    if (!optimalBuild) {
-      let hasMixins = !cssBuild && detectMixin(cssText);
-      let ast = parse(cssText);
-      // only run the applyshim transforms if there is a mixin involved
-      if (hasMixins && nativeCssVariables && this._applyShim) {
-        this._applyShim['transformRules'](ast, elementName);
-      }
-      template['_styleAst'] = ast;
-    }
-    let ownPropertyNames = [];
-    if (!nativeCssVariables) {
-      ownPropertyNames = StyleProperties.decorateStyles(template['_styleAst']);
-    }
-    if (!ownPropertyNames.length || nativeCssVariables) {
-      let root = nativeShadow ? template.content : null;
-      let placeholder = getStylePlaceholder(elementName);
-      let style = this._generateStaticStyle(info, template['_styleAst'], root, placeholder, cssBuild, optimalBuild ? cssText : '');
-      template._style = style;
-    }
-    template._ownPropertyNames = ownPropertyNames;
-  }
-
-  /**
-   * @param {!Array<string>} cssTextArray
-   * @param {string} elementName
-   */
-  prepareAdoptedCssText(cssTextArray, elementName) {
-    adoptedCssTextMap[elementName] = cssTextArray.join(' ');
-  }
-  /**
-   * Prepare template for the given element type
-   * @param {!HTMLTemplateElement} template
-   * @param {string} elementName
-   */
-  prepareTemplateDom(template, elementName) {
-    if (disableRuntime) {
-      return;
-    }
-    const cssBuild = StyleUtil.getCssBuild(template);
-    if (!nativeShadow && cssBuild !== 'shady' && !template._domPrepared) {
-      template._domPrepared = true;
-      StyleTransformer.domAddScope(template.content, elementName);
-    }
-  }
-  /**
-   * @param {!{is: string, extends: (string|undefined)}} info
-   * @param {!StyleNode} rules
-   * @param {DocumentFragment} shadowroot
-   * @param {Node} placeholder
-   * @param {string} cssBuild
-   * @param {string=} cssText
-   * @return {?HTMLStyleElement}
-   */
-  _generateStaticStyle(info, rules, shadowroot, placeholder, cssBuild, cssText) {
-    cssText = StyleTransformer.elementStyles(info, rules, null, cssBuild, cssText);
-    if (cssText.length) {
-      return StyleUtil.applyCss(cssText, info.is, shadowroot, placeholder);
-    }
-    return null;
-  }
-  _prepareHost(host) {
-    const {is, typeExtension} = StyleUtil.getIsExtends(host);
-    const placeholder = getStylePlaceholder(is);
-    const template = templateMap[is];
-    if (!template) {
-      return;
-    }
-    const ast = template['_styleAst'];
-    const ownStylePropertyNames = template._ownPropertyNames;
-    const cssBuild = StyleUtil.getCssBuild(template);
-    const styleInfo = new StyleInfo(
-      ast,
-      placeholder,
-      ownStylePropertyNames,
-      is,
-      typeExtension,
-      cssBuild
-    );
-    StyleInfo.set(host, styleInfo);
-    return styleInfo;
-  }
-  _ensureApplyShim() {
-    if (this._applyShim) {
-      return;
-    } else if (window.ShadyCSS && window.ShadyCSS.ApplyShim) {
-      this._applyShim = /** @type {!Object} */ (window.ShadyCSS.ApplyShim);
-      this._applyShim['invalidCallback'] = ApplyShimUtils.invalidate;
-    }
-  }
-  _ensureCustomStyleInterface() {
-    if (this._customStyleInterface) {
-      return;
-    } else if (window.ShadyCSS && window.ShadyCSS.CustomStyleInterface) {
-      this._customStyleInterface = /** @type {!CustomStyleInterfaceInterface} */(window.ShadyCSS.CustomStyleInterface);
-      /** @type {function(!HTMLStyleElement)} */
-      this._customStyleInterface['transformCallback'] = (style) => {this.transformCustomStyleForDocument(style)};
-      this._customStyleInterface['validateCallback'] = () => {
-        requestAnimationFrame(() => {
-          if (this._customStyleInterface['enqueued'] || this._elementsHaveApplied) {
-            this.flushCustomStyles();
-          }
-        })
-      };
-    }
-  }
-  _ensure() {
-    this._ensureApplyShim();
-    this._ensureCustomStyleInterface();
-  }
-  /**
-   * Flush and apply custom styles to document
-   */
-  flushCustomStyles() {
-    if (disableRuntime) {
-      return;
-    }
-    this._ensure();
-    if (!this._customStyleInterface) {
-      return;
-    }
-    let customStyles = this._customStyleInterface['processStyles']();
-    // early return if custom-styles don't need validation
-    if (!this._customStyleInterface['enqueued']) {
-      return;
-    }
-    // bail if custom styles are built optimally
-    if (StyleUtil.isOptimalCssBuild(this._documentOwnerStyleInfo.cssBuild)) {
-      return;
-    }
-    if (!nativeCssVariables) {
-      this._updateProperties(this._documentOwner, this._documentOwnerStyleInfo);
-      this._applyCustomStyles(customStyles);
-      if (this._elementsHaveApplied) {
-        // if custom elements have upgraded and there are no native css variables, we must recalculate the whole tree
-        this.styleDocument();
-      }
-    } else if (!this._documentOwnerStyleInfo.cssBuild) {
-      this._revalidateCustomStyleApplyShim(customStyles);
-    }
-    this._customStyleInterface['enqueued'] = false;
-  }
-  /**
-   * Apply styles for the given element
-   *
-   * @param {!HTMLElement} host
-   * @param {Object=} overrideProps
-   */
-  styleElement(host, overrideProps) {
-    if (disableRuntime) {
-      if (overrideProps) {
-        if (!StyleInfo.get(host)) {
-          StyleInfo.set(host, new StyleInfo(null));
-        }
-        const styleInfo = /** @type {!StyleInfo} */(StyleInfo.get(host));
-        this._mixOverrideStyleProps(styleInfo, overrideProps);
-        this.styleElementNativeVariables(host, styleInfo);
-      }
-      return;
-    }
-    const styleInfo = StyleInfo.get(host) || this._prepareHost(host);
-    // if there is no style info at this point, bail
-    if (!styleInfo) {
-      return;
-    }
-    // Only trip the `elementsHaveApplied` flag if a node other that the root document has `applyStyle` called
-    if (!this._isRootOwner(host)) {
-      this._elementsHaveApplied = true;
-    }
-    if (overrideProps) {
-      this._mixOverrideStyleProps(styleInfo, overrideProps);
-    }
-    if (!nativeCssVariables) {
-      this.styleElementShimVariables(host, styleInfo);
-    } else {
-      this.styleElementNativeVariables(host, styleInfo);
-    }
-  }
-  /**
-   * @param {!StyleInfo} styleInfo
-   * @param {Object} overrideProps
-   */
-  _mixOverrideStyleProps(styleInfo, overrideProps) {
-    styleInfo.overrideStyleProperties =
-      styleInfo.overrideStyleProperties || {};
-    Object.assign(styleInfo.overrideStyleProperties, overrideProps);
-  }
-  /**
-   * @param {!HTMLElement} host
-   * @param {!StyleInfo} styleInfo
-   */
-  styleElementShimVariables(host, styleInfo) {
-    this.flush();
-    this._updateProperties(host, styleInfo);
-    if (styleInfo.ownStylePropertyNames && styleInfo.ownStylePropertyNames.length) {
-      this._applyStyleProperties(host, styleInfo);
-    }
-  }
-  /**
-   * @param {!HTMLElement} host
-   * @param {!StyleInfo} styleInfo
-   */
-  styleElementNativeVariables(host, styleInfo) {
-    const { is } = StyleUtil.getIsExtends(host);
-    if (styleInfo.overrideStyleProperties) {
-      updateNativeProperties(host, styleInfo.overrideStyleProperties);
-    }
-    const template = templateMap[is];
-    // bail early if there is no shadowroot for this element
-    if (!template && !this._isRootOwner(host)) {
-      return;
-    }
-    // bail early if the template was built with polymer-css-build
-    if (template && StyleUtil.elementHasBuiltCss(template)) {
-      return;
-    }
-    if (template && template._style && !ApplyShimUtils.templateIsValid(template)) {
-      // update template
-      if (!ApplyShimUtils.templateIsValidating(template)) {
-        this._ensure();
-        this._applyShim && this._applyShim['transformRules'](template['_styleAst'], is);
-        template._style.textContent = StyleTransformer.elementStyles(host, styleInfo.styleRules);
-        ApplyShimUtils.startValidatingTemplate(template);
-      }
-      // update instance if native shadowdom
-      if (nativeShadow) {
-        let root = host.shadowRoot;
-        if (root) {
-          let style = root.querySelector('style');
-          if (style) {
-            style.textContent = StyleTransformer.elementStyles(host, styleInfo.styleRules);
-          }
-        }
-      }
-      styleInfo.styleRules = template['_styleAst'];
-    }
-  }
-  _styleOwnerForNode(node) {
-    let root = StyleUtil.wrap(node).getRootNode();
-    let host = root.host;
-    if (host) {
-      if (StyleInfo.get(host) || this._prepareHost(host)) {
-        return host;
-      } else {
-        return this._styleOwnerForNode(host);
-      }
-    }
-    return this._documentOwner;
-  }
-  _isRootOwner(node) {
-    return (node === this._documentOwner);
-  }
-  _applyStyleProperties(host, styleInfo) {
-    let is = StyleUtil.getIsExtends(host).is;
-    let cacheEntry = styleCache.fetch(is, styleInfo.styleProperties, styleInfo.ownStylePropertyNames);
-    let cachedScopeSelector = cacheEntry && cacheEntry.scopeSelector;
-    let cachedStyle = cacheEntry ? cacheEntry.styleElement : null;
-    let oldScopeSelector = styleInfo.scopeSelector;
-    // only generate new scope if cached style is not found
-    styleInfo.scopeSelector = cachedScopeSelector || this._generateScopeSelector(is);
-    let style = StyleProperties.applyElementStyle(host, styleInfo.styleProperties, styleInfo.scopeSelector, cachedStyle);
-    if (!nativeShadow) {
-      StyleProperties.applyElementScopeSelector(host, styleInfo.scopeSelector, oldScopeSelector);
-    }
-    if (!cacheEntry) {
-      styleCache.store(is, styleInfo.styleProperties, style, styleInfo.scopeSelector);
-    }
-    return style;
-  }
-  _updateProperties(host, styleInfo) {
-    let owner = this._styleOwnerForNode(host);
-    let ownerStyleInfo = StyleInfo.get(owner);
-    let ownerProperties = ownerStyleInfo.styleProperties;
-    // style owner has not updated properties yet
-    // go up the chain and force property update,
-    // except if the owner is the document
-    if (owner !== this._documentOwner && !ownerProperties) {
-      this._updateProperties(owner, ownerStyleInfo);
-      ownerProperties = ownerStyleInfo.styleProperties;
-    }
-    let props = Object.create(ownerProperties || null);
-    let hostAndRootProps = StyleProperties.hostAndRootPropertiesForScope(host, styleInfo.styleRules, styleInfo.cssBuild);
-    let propertyData = StyleProperties.propertyDataFromStyles(ownerStyleInfo.styleRules, host);
-    let propertiesMatchingHost = propertyData.properties
-    Object.assign(
-      props,
-      hostAndRootProps.hostProps,
-      propertiesMatchingHost,
-      hostAndRootProps.rootProps
-    );
-    this._mixinOverrideStyles(props, styleInfo.overrideStyleProperties);
-    StyleProperties.reify(props);
-    styleInfo.styleProperties = props;
-  }
-  _mixinOverrideStyles(props, overrides) {
-    for (let p in overrides) {
-      let v = overrides[p];
-      // skip override props if they are not truthy or 0
-      // in order to fall back to inherited values
-      if (v || v === 0) {
-        props[p] = v;
-      }
-    }
-  }
-  /**
-   * Update styles of the whole document
-   *
-   * @param {Object=} properties
-   */
-  styleDocument(properties) {
-    this.styleSubtree(this._documentOwner, properties);
-  }
-  /**
-   * Update styles of a subtree
-   *
-   * @param {!HTMLElement} host
-   * @param {Object=} properties
-   */
-  styleSubtree(host, properties) {
-    const wrappedHost = StyleUtil.wrap(host);
-    let root = wrappedHost.shadowRoot;
-    if (root || this._isRootOwner(host)) {
-      this.styleElement(host, properties);
-    }
-    // process the shadowdom children of `host`
-    let shadowChildren =
-        root && (/** @type {!ParentNode} */ (root).children || root.childNodes);
-    if (shadowChildren) {
-      for (let i = 0; i < shadowChildren.length; i++) {
-        let c = /** @type {!HTMLElement} */(shadowChildren[i]);
-        this.styleSubtree(c);
-      }
-    } else {
-      // process the lightdom children of `host`
-      let children = wrappedHost.children || wrappedHost.childNodes;
-      if (children) {
-        for (let i = 0; i < children.length; i++) {
-          let c = /** @type {!HTMLElement} */(children[i]);
-          this.styleSubtree(c);
-        }
-      }
-    }
-  }
-  /* Custom Style operations */
-  _revalidateCustomStyleApplyShim(customStyles) {
-    for (let i = 0; i < customStyles.length; i++) {
-      let c = customStyles[i];
-      let s = this._customStyleInterface['getStyleForCustomStyle'](c);
-      if (s) {
-        this._revalidateApplyShim(s);
-      }
-    }
-  }
-  _applyCustomStyles(customStyles) {
-    for (let i = 0; i < customStyles.length; i++) {
-      let c = customStyles[i];
-      let s = this._customStyleInterface['getStyleForCustomStyle'](c);
-      if (s) {
-        StyleProperties.applyCustomStyle(s, this._documentOwnerStyleInfo.styleProperties);
-      }
-    }
-  }
-  transformCustomStyleForDocument(style) {
-    const cssBuild = StyleUtil.getCssBuild(style);
-    if (cssBuild !== this._documentOwnerStyleInfo.cssBuild) {
-      this._documentOwnerStyleInfo.cssBuild = cssBuild;
-    }
-    if (StyleUtil.isOptimalCssBuild(cssBuild)) {
-      return;
-    }
-    let ast = StyleUtil.rulesForStyle(style);
-    StyleUtil.forEachRule(ast, (rule) => {
-      if (nativeShadow) {
-        StyleTransformer.normalizeRootSelector(rule);
-      } else {
-        StyleTransformer.documentRule(rule);
-      }
-      if (nativeCssVariables && cssBuild === '') {
-        this._ensure();
-        this._applyShim && this._applyShim['transformRule'](rule);
-      }
-    });
-    if (nativeCssVariables) {
-      style.textContent = StyleUtil.toCssText(ast);
-    } else {
-      this._documentOwnerStyleInfo.styleRules['rules'].push(ast);
-    }
-  }
-  _revalidateApplyShim(style) {
-    if (nativeCssVariables && this._applyShim) {
-      let ast = StyleUtil.rulesForStyle(style);
-      this._ensure();
-      this._applyShim['transformRules'](ast);
-      style.textContent = StyleUtil.toCssText(ast);
-    }
-  }
-  getComputedStyleValue(element, property) {
-    let value;
-    if (!nativeCssVariables) {
-      // element is either a style host, or an ancestor of a style host
-      let styleInfo = StyleInfo.get(element) || StyleInfo.get(this._styleOwnerForNode(element));
-      value = styleInfo.styleProperties[property];
-    }
-    // fall back to the property value from the computed styling
-    value = value || window.getComputedStyle(element).getPropertyValue(property);
-    // trim whitespace that can come after the `:` in css
-    // example: padding: 2px -> " 2px"
-    return value ? value.trim() : '';
-  }
-  // given an element and a classString, replaces
-  // the element's class with the provided classString and adds
-  // any necessary ShadyCSS static and property based scoping selectors
-  setElementClass(element, classString) {
-    let root = StyleUtil.wrap(element).getRootNode();
-    let classes = classString ? classString.split(/\s/) : [];
-    let scopeName = root.host && root.host.localName;
-    // If no scope, try to discover scope name from existing class.
-    // This can occur if, for example, a template stamped element that
-    // has been scoped is manipulated when not in a root.
-    if (!scopeName) {
-      var classAttr = element.getAttribute('class');
-      if (classAttr) {
-        let k$ = classAttr.split(/\s/);
-        for (let i=0; i < k$.length; i++) {
-          if (k$[i] === StyleTransformer.SCOPE_NAME) {
-            scopeName = k$[i+1];
-            break;
-          }
-        }
-      }
-    }
-    if (scopeName) {
-      classes.push(StyleTransformer.SCOPE_NAME, scopeName);
-    }
-    if (!nativeCssVariables) {
-      let styleInfo = StyleInfo.get(element);
-      if (styleInfo && styleInfo.scopeSelector) {
-        classes.push(StyleProperties.XSCOPE_NAME, styleInfo.scopeSelector);
-      }
-    }
-    StyleUtil.setElementClassRaw(element, classes.join(' '));
-  }
-  _styleInfoForNode(node) {
-    return StyleInfo.get(node);
-  }
-  /**
-   * @param {!Element} node
-   * @param {string} scope
-   */
-  scopeNode(node, scope) {
-    StyleTransformer.element(node, scope);
-  }
-  /**
-   * @param {!Element} node
-   * @param {string} scope
-   */
-  unscopeNode(node, scope) {
-    StyleTransformer.element(node, scope, true);
-  }
-  /**
-   * @param {!Node} node
-   * @return {string}
-   */
-  scopeForNode(node) {
-    return getOwnerScope(node);
-  }
-  /**
-   * @param {!Element} node
-   * @return {string}
-   */
-  currentScopeForNode(node) {
-    return getCurrentScope(node);
-  }
-}
-
-/* exports */
-/* eslint-disable no-self-assign */
-ScopingShim.prototype['flush'] = ScopingShim.prototype.flush;
-ScopingShim.prototype['prepareTemplate'] = ScopingShim.prototype.prepareTemplate;
-ScopingShim.prototype['styleElement'] = ScopingShim.prototype.styleElement;
-ScopingShim.prototype['styleDocument'] = ScopingShim.prototype.styleDocument;
-ScopingShim.prototype['styleSubtree'] = ScopingShim.prototype.styleSubtree;
-ScopingShim.prototype['getComputedStyleValue'] = ScopingShim.prototype.getComputedStyleValue;
-ScopingShim.prototype['setElementClass'] = ScopingShim.prototype.setElementClass;
-ScopingShim.prototype['_styleInfoForNode'] = ScopingShim.prototype._styleInfoForNode;
-ScopingShim.prototype['transformCustomStyleForDocument'] = ScopingShim.prototype.transformCustomStyleForDocument;
-ScopingShim.prototype['getStyleAst'] = ScopingShim.prototype.getStyleAst;
-ScopingShim.prototype['styleAstToString'] = ScopingShim.prototype.styleAstToString;
-ScopingShim.prototype['flushCustomStyles'] = ScopingShim.prototype.flushCustomStyles;
-ScopingShim.prototype['scopeNode'] = ScopingShim.prototype.scopeNode;
-ScopingShim.prototype['unscopeNode'] = ScopingShim.prototype.unscopeNode;
-ScopingShim.prototype['scopeForNode'] = ScopingShim.prototype.scopeForNode;
-ScopingShim.prototype['currentScopeForNode'] = ScopingShim.prototype.currentScopeForNode;
-ScopingShim.prototype['prepareAdoptedCssText'] = ScopingShim.prototype.prepareAdoptedCssText;
-/* eslint-enable no-self-assign */
-Object.defineProperties(ScopingShim.prototype, {
-  'nativeShadow': {
-    get() {
-      return nativeShadow;
-    }
-  },
-  'nativeCss': {
-    get() {
-      return nativeCssVariables;
-    }
-  }
-});
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-cache.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-cache.js
deleted file mode 100644
index 0006a0b..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-cache.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-'use strict';
-
-export default class StyleCache {
-  constructor(typeMax = 100) {
-    // map element name -> [{properties, styleElement, scopeSelector}]
-    this.cache = {};
-    /** @type {number} */
-    this.typeMax = typeMax;
-  }
-
-  _validate(cacheEntry, properties, ownPropertyNames) {
-    for (let idx = 0; idx < ownPropertyNames.length; idx++) {
-      let pn = ownPropertyNames[idx];
-      if (cacheEntry.properties[pn] !== properties[pn]) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  store(tagname, properties, styleElement, scopeSelector) {
-    let list = this.cache[tagname] || [];
-    list.push({properties, styleElement, scopeSelector});
-    if (list.length > this.typeMax) {
-      list.shift();
-    }
-    this.cache[tagname] = list;
-  }
-
-  fetch(tagname, properties, ownPropertyNames) {
-    let list = this.cache[tagname];
-    if (!list) {
-      return;
-    }
-    // reverse list for most-recent lookups
-    for (let idx = list.length - 1; idx >= 0; idx--) {
-      let entry = list[idx];
-      if (this._validate(entry, properties, ownPropertyNames)) {
-        return entry;
-      }
-    }
-  }
-}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-info.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-info.js
deleted file mode 100644
index deab329..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-info.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
-
-/** @const {string} */
-const infoKey = '__styleInfo';
-
-export default class StyleInfo {
-  /**
-   * @param {Element} node
-   * @return {StyleInfo}
-   */
-  static get(node) {
-    if (node) {
-      return node[infoKey];
-    } else {
-      return null;
-    }
-  }
-  /**
-   * @param {!Element} node
-   * @param {StyleInfo} styleInfo
-   * @return {StyleInfo}
-   */
-  static set(node, styleInfo) {
-    node[infoKey] = styleInfo;
-    return styleInfo;
-  }
-  /**
-   * @param {StyleNode} ast
-   * @param {Node=} placeholder
-   * @param {Array<string>=} ownStylePropertyNames
-   * @param {string=} elementName
-   * @param {string=} typeExtension
-   * @param {string=} cssBuild
-   */
-  constructor(ast, placeholder, ownStylePropertyNames, elementName, typeExtension, cssBuild) {
-    /** @type {StyleNode} */
-    this.styleRules = ast || null;
-    /** @type {Node} */
-    this.placeholder = placeholder || null;
-    /** @type {!Array<string>} */
-    this.ownStylePropertyNames = ownStylePropertyNames || [];
-    /** @type {Object} */
-    this.overrideStyleProperties = null;
-    /** @type {string} */
-    this.elementName = elementName || '';
-    /** @type {string} */
-    this.cssBuild = cssBuild || '';
-    /** @type {string} */
-    this.typeExtension = typeExtension || '';
-    /** @type {Object<string, string>} */
-    this.styleProperties = null;
-    /** @type {?string} */
-    this.scopeSelector = null;
-    /** @type {HTMLStyleElement} */
-    this.customStyle = null;
-  }
-  _getStyleRules() {
-    return this.styleRules;
-  }
-}
-
-/* eslint-disable-next-line no-self-assign */
-StyleInfo.prototype['_getStyleRules'] = StyleInfo.prototype._getStyleRules;
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-placeholder.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-placeholder.js
deleted file mode 100644
index d003379..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-placeholder.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import {applyStylePlaceHolder} from './style-util.js';
-import {nativeShadow, disableRuntime} from './style-settings.js';
-
-/** @type {!Object<string, !Node>} */
-const placeholderMap = {};
-
-/**
- * @param {string} elementName
- * @return {Node}
- */
-export function getStylePlaceholder(elementName) {
-  return placeholderMap[elementName] || null;
-}
-
-/**
- * @param {string} elementName
- */
-export function ensureStylePlaceholder(elementName) {
-  if (!placeholderMap[elementName]) {
-    placeholderMap[elementName] = applyStylePlaceHolder(elementName);
-  }
-}
-
-/**
- * @const {CustomElementRegistry}
- */
-const ce = window['customElements'];
-if (ce && !nativeShadow && !disableRuntime) {
-  /**
-   * @const {function(this:CustomElementRegistry, string,function(new:HTMLElement),{extends: string}=)}
-   */
-  const origDefine = ce['define'];
-  /**
-   * @param {string} name
-   * @param {function(new:HTMLElement)} clazz
-   * @param {{extends: string}=} options
-   */
-  const wrappedDefine = (name, clazz, options) => {
-    ensureStylePlaceholder(name);
-    origDefine.call(/** @type {!CustomElementRegistry} */(ce), name, clazz, options);
-  };
-  ce['define'] = wrappedDefine;
-}
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-properties.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-properties.js
deleted file mode 100644
index ecaa389..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-properties.js
+++ /dev/null
@@ -1,608 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import {removeCustomPropAssignment, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
-import {nativeShadow} from './style-settings.js';
-import StyleTransformer from './style-transformer.js';
-import * as StyleUtil from './style-util.js';
-import * as RX from './common-regex.js';
-import StyleInfo from './style-info.js';
-
-// TODO: dedupe with shady
-/**
- * @param {string} selector
- * @return {boolean}
- * @this {Element}
- */
-const matchesSelector = function(selector) {
-  const method = this.matches || this.matchesSelector ||
-    this.mozMatchesSelector || this.msMatchesSelector ||
-    this.oMatchesSelector || this.webkitMatchesSelector;
-  return method && method.call(this, selector);
-};
-
-const IS_IE = navigator.userAgent.match('Trident');
-
-const XSCOPE_NAME = 'x-scope';
-
-class StyleProperties {
-  get XSCOPE_NAME() {
-    return XSCOPE_NAME;
-  }
-/**
- * decorates styles with rule info and returns an array of used style property names
- *
- * @param {StyleNode} rules
- * @return {Array<string>}
- */
-  decorateStyles(rules) {
-    let self = this, props = {}, keyframes = [], ruleIndex = 0;
-    StyleUtil.forEachRule(rules, function(rule) {
-      self.decorateRule(rule);
-      // mark in-order position of ast rule in styles block, used for cache key
-      rule.index = ruleIndex++;
-      self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
-    }, function onKeyframesRule(rule) {
-      keyframes.push(rule);
-    });
-    // Cache all found keyframes rules for later reference:
-    rules._keyframes = keyframes;
-    // return this list of property names *consumes* in these styles.
-    let names = [];
-    for (let i in props) {
-      names.push(i);
-    }
-    return names;
-  }
-
-  // decorate a single rule with property info
-  decorateRule(rule) {
-    if (rule.propertyInfo) {
-      return rule.propertyInfo;
-    }
-    let info = {}, properties = {};
-    let hasProperties = this.collectProperties(rule, properties);
-    if (hasProperties) {
-      info.properties = properties;
-      // TODO(sorvell): workaround parser seeing mixins as additional rules
-      rule['rules'] = null;
-    }
-    info.cssText = this.collectCssText(rule);
-    rule.propertyInfo = info;
-    return info;
-  }
-
-  // collects the custom properties from a rule's cssText
-  collectProperties(rule, properties) {
-    let info = rule.propertyInfo;
-    if (info) {
-      if (info.properties) {
-        Object.assign(properties, info.properties);
-        return true;
-      }
-    } else {
-      let m, rx = RX.VAR_ASSIGN;
-      let cssText = rule['parsedCssText'];
-      let value;
-      let any;
-      while ((m = rx.exec(cssText))) {
-        // note: group 2 is var, 3 is mixin
-        value = (m[2] || m[3]).trim();
-        // value of 'inherit' or 'unset' is equivalent to not setting the property here
-        if (value !== 'inherit' || value !== 'unset') {
-          properties[m[1].trim()] = value;
-        }
-        any = true;
-      }
-      return any;
-    }
-
-  }
-
-  // returns cssText of properties that consume variables/mixins
-  collectCssText(rule) {
-    return this.collectConsumingCssText(rule['parsedCssText']);
-  }
-
-  // NOTE: we support consumption inside mixin assignment
-  // but not production, so strip out {...}
-  collectConsumingCssText(cssText) {
-    return cssText.replace(RX.BRACKETED, '')
-      .replace(RX.VAR_ASSIGN, '');
-  }
-
-  collectPropertiesInCssText(cssText, props) {
-    let m;
-    while ((m = RX.VAR_CONSUMED.exec(cssText))) {
-      let name = m[1];
-      // This regex catches all variable names, and following non-whitespace char
-      // If next char is not ':', then variable is a consumer
-      if (m[2] !== ':') {
-        props[name] = true;
-      }
-    }
-  }
-
-  // turns custom properties into realized values.
-  reify(props) {
-    // big perf optimization here: reify only *own* properties
-    // since this object has __proto__ of the element's scope properties
-    let names = Object.getOwnPropertyNames(props);
-    for (let i=0, n; i < names.length; i++) {
-      n = names[i];
-      props[n] = this.valueForProperty(props[n], props);
-    }
-  }
-
-  // given a property value, returns the reified value
-  // a property value may be:
-  // (1) a literal value like: red or 5px;
-  // (2) a variable value like: var(--a), var(--a, red), or var(--a, --b) or
-  // var(--a, var(--b));
-  // (3) a literal mixin value like { properties }. Each of these properties
-  // can have values that are: (a) literal, (b) variables, (c) @apply mixins.
-  valueForProperty(property, props) {
-    // case (1) default
-    // case (3) defines a mixin and we have to reify the internals
-    if (property) {
-      if (property.indexOf(';') >=0) {
-        property = this.valueForProperties(property, props);
-      } else {
-        // case (2) variable
-        let self = this;
-        let fn = function(prefix, value, fallback, suffix) {
-          if (!value) {
-            return prefix + suffix;
-          }
-          let propertyValue = self.valueForProperty(props[value], props);
-          // if value is "initial", then the variable should be treated as unset
-          if (!propertyValue || propertyValue === 'initial') {
-            // fallback may be --a or var(--a) or literal
-            propertyValue = self.valueForProperty(props[fallback] || fallback, props) ||
-            fallback;
-          } else if (propertyValue === 'apply-shim-inherit') {
-            // CSS build will replace `inherit` with `apply-shim-inherit`
-            // for use with native css variables.
-            // Since we have full control, we can use `inherit` directly.
-            propertyValue = 'inherit';
-          }
-          return prefix + (propertyValue || '') + suffix;
-        };
-        property = StyleUtil.processVariableAndFallback(property, fn);
-      }
-    }
-    return property && property.trim() || '';
-  }
-
-  // note: we do not yet support mixin within mixin
-  valueForProperties(property, props) {
-    let parts = property.split(';');
-    for (let i=0, p, m; i<parts.length; i++) {
-      if ((p = parts[i])) {
-        RX.MIXIN_MATCH.lastIndex = 0;
-        m = RX.MIXIN_MATCH.exec(p);
-        if (m) {
-          p = this.valueForProperty(props[m[1]], props);
-        } else {
-          let colon = p.indexOf(':');
-          if (colon !== -1) {
-            let pp = p.substring(colon);
-            pp = pp.trim();
-            pp = this.valueForProperty(pp, props) || pp;
-            p = p.substring(0, colon) + pp;
-          }
-        }
-        parts[i] = (p && p.lastIndexOf(';') === p.length - 1) ?
-          // strip trailing ;
-          p.slice(0, -1) :
-          p || '';
-      }
-    }
-    return parts.join(';');
-  }
-
-  applyProperties(rule, props) {
-    let output = '';
-    // dynamically added sheets may not be decorated so ensure they are.
-    if (!rule.propertyInfo) {
-      this.decorateRule(rule);
-    }
-    if (rule.propertyInfo.cssText) {
-      output = this.valueForProperties(rule.propertyInfo.cssText, props);
-    }
-    rule['cssText'] = output;
-  }
-
-  // Apply keyframe transformations to the cssText of a given rule. The
-  // keyframeTransforms object is a map of keyframe names to transformer
-  // functions which take in cssText and spit out transformed cssText.
-  applyKeyframeTransforms(rule, keyframeTransforms) {
-    let input = rule['cssText'];
-    let output = rule['cssText'];
-    if (rule.hasAnimations == null) {
-      // Cache whether or not the rule has any animations to begin with:
-      rule.hasAnimations = RX.ANIMATION_MATCH.test(input);
-    }
-    // If there are no animations referenced, we can skip transforms:
-    if (rule.hasAnimations) {
-      let transform;
-      // If we haven't transformed this rule before, we iterate over all
-      // transforms:
-      if (rule.keyframeNamesToTransform == null) {
-        rule.keyframeNamesToTransform = [];
-        for (let keyframe in keyframeTransforms) {
-          transform = keyframeTransforms[keyframe];
-          output = transform(input);
-          // If the transform actually changed the CSS text, we cache the
-          // transform name for future use:
-          if (input !== output) {
-            input = output;
-            rule.keyframeNamesToTransform.push(keyframe);
-          }
-        }
-      } else {
-        // If we already have a list of keyframe names that apply to this
-        // rule, we apply only those keyframe name transforms:
-        for (let i = 0; i < rule.keyframeNamesToTransform.length; ++i) {
-          transform = keyframeTransforms[rule.keyframeNamesToTransform[i]];
-          input = transform(input);
-        }
-        output = input;
-      }
-    }
-    rule['cssText'] = output;
-  }
-
-  // Test if the rules in these styles matches the given `element` and if so,
-  // collect any custom properties into `props`.
-  /**
-   * @param {StyleNode} rules
-   * @param {Element} element
-   */
-  propertyDataFromStyles(rules, element) {
-    let props = {};
-    // generates a unique key for these matches
-    let o = [];
-    // note: active rules excludes non-matching @media rules
-    StyleUtil.forEachRule(rules, (rule) => {
-      // TODO(sorvell): we could trim the set of rules at declaration
-      // time to only include ones that have properties
-      if (!rule.propertyInfo) {
-        this.decorateRule(rule);
-      }
-      // match element against transformedSelector: selector may contain
-      // unwanted uniquification and parsedSelector does not directly match
-      // for :host selectors.
-      let selectorToMatch = rule.transformedSelector || rule['parsedSelector'];
-      if (element && rule.propertyInfo.properties && selectorToMatch) {
-        if (matchesSelector.call(element, selectorToMatch)) {
-          this.collectProperties(rule, props);
-          // produce numeric key for these matches for lookup
-          addToBitMask(rule.index, o);
-        }
-      }
-    }, null, true);
-    return {properties: props, key: o};
-  }
-
-  /**
-   * @param {Element} scope
-   * @param {StyleNode} rule
-   * @param {string} cssBuild
-   * @param {function(Object)} callback
-   */
-  whenHostOrRootRule(scope, rule, cssBuild, callback) {
-    if (!rule.propertyInfo) {
-      this.decorateRule(rule);
-    }
-    if (!rule.propertyInfo.properties) {
-      return;
-    }
-    let {is, typeExtension} = StyleUtil.getIsExtends(scope);
-    let hostScope = is ?
-      StyleTransformer._calcHostScope(is, typeExtension) :
-      'html';
-    let parsedSelector = rule['parsedSelector'];
-    let isRoot = (parsedSelector === ':host > *' || parsedSelector === 'html');
-    let isHost = parsedSelector.indexOf(':host') === 0 && !isRoot;
-    // build info is either in scope (when scope is an element) or in the style
-    // when scope is the default scope; note: this allows default scope to have
-    // mixed mode built and unbuilt styles.
-    if (cssBuild === 'shady') {
-      // :root -> x-foo > *.x-foo for elements and html for custom-style
-      isRoot = parsedSelector === (hostScope + ' > *.' + hostScope) || parsedSelector.indexOf('html') !== -1;
-      // :host -> x-foo for elements, but sub-rules have .x-foo in them
-      isHost = !isRoot && parsedSelector.indexOf(hostScope) === 0;
-    }
-    if (!isRoot && !isHost) {
-      return;
-    }
-    let selectorToMatch = hostScope;
-    if (isHost) {
-      // need to transform :host because `:host` does not work with `matches`
-      if (!rule.transformedSelector) {
-        // transform :host into a matchable selector
-        rule.transformedSelector =
-        StyleTransformer._transformRuleCss(
-          rule,
-          StyleTransformer._transformComplexSelector,
-          StyleTransformer._calcElementScope(is),
-          hostScope
-        );
-      }
-      selectorToMatch = rule.transformedSelector || hostScope;
-    }
-    callback({
-      selector: selectorToMatch,
-      isHost: isHost,
-      isRoot: isRoot
-    });
-  }
-/**
- * @param {Element} scope
- * @param {StyleNode} rules
- * @param {string} cssBuild
- * @return {Object}
- */
-  hostAndRootPropertiesForScope(scope, rules, cssBuild) {
-    let hostProps = {}, rootProps = {};
-    // note: active rules excludes non-matching @media rules
-    StyleUtil.forEachRule(rules, (rule) => {
-      // if scope is StyleDefaults, use _element for matchesSelector
-      this.whenHostOrRootRule(scope, rule, cssBuild, (info) => {
-        let element = scope._element || scope;
-        if (matchesSelector.call(element, info.selector)) {
-          if (info.isHost) {
-            this.collectProperties(rule, hostProps);
-          } else {
-            this.collectProperties(rule, rootProps);
-          }
-        }
-      });
-    }, null, true);
-    return {rootProps: rootProps, hostProps: hostProps};
-  }
-
-  /**
-   * @param {Element} element
-   * @param {Object} properties
-   * @param {string} scopeSelector
-   */
-  transformStyles(element, properties, scopeSelector) {
-    let self = this;
-    let {is, typeExtension} = StyleUtil.getIsExtends(element);
-    let hostSelector = StyleTransformer
-      ._calcHostScope(is, typeExtension);
-    let rxHostSelector = element.extends ?
-      '\\' + hostSelector.slice(0, -1) + '\\]' :
-      hostSelector;
-    let hostRx = new RegExp(RX.HOST_PREFIX + rxHostSelector +
-      RX.HOST_SUFFIX);
-    let {styleRules: rules, cssBuild} = StyleInfo.get(element);
-    let keyframeTransforms =
-      this._elementKeyframeTransforms(element, rules, scopeSelector);
-    return StyleTransformer.elementStyles(element, rules, function(rule) {
-      self.applyProperties(rule, properties);
-      if (!nativeShadow &&
-          !StyleUtil.isKeyframesSelector(rule) &&
-          rule['cssText']) {
-        // NOTE: keyframe transforms only scope munge animation names, so it
-        // is not necessary to apply them in ShadowDOM.
-        self.applyKeyframeTransforms(rule, keyframeTransforms);
-        self._scopeSelector(rule, hostRx, hostSelector, scopeSelector);
-      }
-    }, cssBuild);
-  }
-
-  /**
-   * @param {Element} element
-   * @param {StyleNode} rules
-   * @param {string} scopeSelector
-   * @return {Object}
-   */
-  _elementKeyframeTransforms(element, rules, scopeSelector) {
-    let keyframesRules = rules._keyframes;
-    let keyframeTransforms = {};
-    if (!nativeShadow && keyframesRules) {
-      // For non-ShadowDOM, we transform all known keyframes rules in
-      // advance for the current scope. This allows us to catch keyframes
-      // rules that appear anywhere in the stylesheet:
-      for (let i = 0, keyframesRule = keyframesRules[i];
-           i < keyframesRules.length;
-           keyframesRule = keyframesRules[++i]) {
-        this._scopeKeyframes(keyframesRule, scopeSelector);
-        keyframeTransforms[keyframesRule['keyframesName']] =
-            this._keyframesRuleTransformer(keyframesRule);
-      }
-    }
-    return keyframeTransforms;
-  }
-
-  // Generate a factory for transforming a chunk of CSS text to handle a
-  // particular scoped keyframes rule.
-  /**
-   * @param {StyleNode} keyframesRule
-   * @return {function(string):string}
-   */
-  _keyframesRuleTransformer(keyframesRule) {
-    return function(cssText) {
-      return cssText.replace(
-          keyframesRule.keyframesNameRx,
-          keyframesRule.transformedKeyframesName);
-    };
-  }
-
-/**
- * Transforms `@keyframes` names to be unique for the current host.
- * Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0
- *
- * @param {StyleNode} rule
- * @param {string} scopeId
- */
-  _scopeKeyframes(rule, scopeId) {
-    // Animation names are of the form [\w-], so ensure that the name regex does not partially apply
-    // to similarly named keyframe names by checking for a word boundary at the beginning and
-    // a non-word boundary or `-` at the end.
-    rule.keyframesNameRx = new RegExp(`\\b${rule['keyframesName']}(?!\\B|-)`, 'g');
-    rule.transformedKeyframesName = rule['keyframesName'] + '-' + scopeId;
-    rule.transformedSelector = rule.transformedSelector || rule['selector'];
-    rule['selector'] = rule.transformedSelector.replace(
-        rule['keyframesName'], rule.transformedKeyframesName);
-  }
-
-  // Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes):
-  // non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo
-  // host selector: x-foo.wide -> .x-foo-42.wide
-  // note: we use only the scope class (.x-foo-42) and not the hostSelector
-  // (x-foo) to scope :host rules; this helps make property host rules
-  // have low specificity. They are overrideable by class selectors but,
-  // unfortunately, not by type selectors (e.g. overriding via
-  // `.special` is ok, but not by `x-foo`).
-  /**
-   * @param {StyleNode} rule
-   * @param {RegExp} hostRx
-   * @param {string} hostSelector
-   * @param {string} scopeId
-   */
-  _scopeSelector(rule, hostRx, hostSelector, scopeId) {
-    rule.transformedSelector = rule.transformedSelector || rule['selector'];
-    let selector = rule.transformedSelector;
-    let scope = '.' + scopeId;
-    let parts = StyleUtil.splitSelectorList(selector);
-    for (let i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
-      parts[i] = p.match(hostRx) ?
-        p.replace(hostSelector, scope) :
-        scope + ' ' + p;
-    }
-    rule['selector'] = parts.join(',');
-  }
-
-  /**
-   * @param {Element} element
-   * @param {string} selector
-   * @param {string} old
-   */
-  applyElementScopeSelector(element, selector, old) {
-    let c = element.getAttribute('class') || '';
-    let v = c;
-    if (old) {
-      v = c.replace(
-        new RegExp('\\s*' + XSCOPE_NAME + '\\s*' + old + '\\s*', 'g'), ' ');
-    }
-    v += (v ? ' ' : '') + XSCOPE_NAME + ' ' + selector;
-    if (c !== v) {
-      StyleUtil.setElementClassRaw(element, v);
-    }
-  }
-
-  /**
-   * @param {HTMLElement} element
-   * @param {Object} properties
-   * @param {string} selector
-   * @param {HTMLStyleElement} style
-   * @return {HTMLStyleElement}
-   */
-  applyElementStyle(element, properties, selector, style) {
-    // calculate cssText to apply
-    let cssText = style ? style.textContent || '' :
-      this.transformStyles(element, properties, selector);
-    // if shady and we have a cached style that is not style, decrement
-    let styleInfo = StyleInfo.get(element);
-    let s = styleInfo.customStyle;
-    if (s && !nativeShadow && (s !== style)) {
-      s['_useCount']--;
-      if (s['_useCount'] <= 0 && s.parentNode) {
-        s.parentNode.removeChild(s);
-      }
-    }
-    // apply styling always under native or if we generated style
-    // or the cached style is not in document(!)
-    if (nativeShadow) {
-      // update existing style only under native
-      if (styleInfo.customStyle) {
-        styleInfo.customStyle.textContent = cssText;
-        style = styleInfo.customStyle;
-      // otherwise, if we have css to apply, do so
-      } else if (cssText) {
-        // apply css after the scope style of the element to help with
-        // style precedence rules.
-        style = StyleUtil.applyCss(cssText, selector, element.shadowRoot,
-          styleInfo.placeholder);
-      }
-    } else {
-      // shady and no cache hit
-      if (!style) {
-        // apply css after the scope style of the element to help with
-        // style precedence rules.
-        if (cssText) {
-          style = StyleUtil.applyCss(cssText, selector, null,
-            styleInfo.placeholder);
-        }
-      // shady and cache hit but not in document
-      } else if (!style.parentNode) {
-        if (IS_IE && cssText.indexOf('@media') > -1) {
-            // @media rules may be stale in IE 10 and 11
-            // refresh the text content of the style to revalidate them.
-          style.textContent = cssText;
-        }
-        StyleUtil.applyStyle(style, null, styleInfo.placeholder);
-      }
-    }
-    // ensure this style is our custom style and increment its use count.
-    if (style) {
-      style['_useCount'] = style['_useCount'] || 0;
-      // increment use count if we changed styles
-      if (styleInfo.customStyle != style) {
-        style['_useCount']++;
-      }
-      styleInfo.customStyle = style;
-    }
-    return style;
-  }
-
-  /**
-   * @param {Element} style
-   * @param {Object} properties
-   */
-  applyCustomStyle(style, properties) {
-    let rules = StyleUtil.rulesForStyle(/** @type {HTMLStyleElement} */(style));
-    let self = this;
-    style.textContent = StyleUtil.toCssText(rules, function(/** StyleNode */rule) {
-      let css = rule['cssText'] = rule['parsedCssText'];
-      if (rule.propertyInfo && rule.propertyInfo.cssText) {
-        // remove property assignments
-        // so next function isn't confused
-        // NOTE: we have 3 categories of css:
-        // (1) normal properties,
-        // (2) custom property assignments (--foo: red;),
-        // (3) custom property usage: border: var(--foo); @apply(--foo);
-        // In elements, 1 and 3 are separated for efficiency; here they
-        // are not and this makes this case unique.
-        css = removeCustomPropAssignment(/** @type {string} */(css));
-        // replace with reified properties, scenario is same as mixin
-        rule['cssText'] = self.valueForProperties(css, properties);
-      }
-    });
-  }
-}
-
-/**
- * @param {number} n
- * @param {Array<number>} bits
- */
-function addToBitMask(n, bits) {
-  let o = parseInt(n / 32, 10);
-  let v = 1 << (n % 32);
-  bits[o] = (bits[o] || 0) | v;
-}
-
-export default new StyleProperties();
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-settings.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-settings.js
deleted file mode 100644
index c8fdd4b..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-settings.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-export const nativeShadow = !(window['ShadyDOM'] && window['ShadyDOM']['inUse']);
-let nativeCssVariables_;
-
-/**
- * @param {(ShadyCSSOptions | ShadyCSSInterface)=} settings
- */
-function calcCssVariables(settings) {
-  if (settings && settings['shimcssproperties']) {
-    nativeCssVariables_ = false;
-  } else {
-    // chrome 49 has semi-working css vars, check if box-shadow works
-    // safari 9.1 has a recalc bug: https://bugs.webkit.org/show_bug.cgi?id=155782
-    // However, shim css custom properties are only supported with ShadyDOM enabled,
-    // so fall back on native if we do not detect ShadyDOM
-    // Edge 15: custom properties used in ::before and ::after will also be used in the parent element
-    // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12414257/
-    nativeCssVariables_ = nativeShadow || Boolean(!navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/) &&
-      window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)'));
-  }
-}
-
-/** @type {string | undefined} */
-export let cssBuild;
-if (window.ShadyCSS && window.ShadyCSS.cssBuild !== undefined) {
-  cssBuild = window.ShadyCSS.cssBuild;
-}
-
-/** @type {boolean} */
-export const disableRuntime = Boolean(window.ShadyCSS && window.ShadyCSS.disableRuntime);
-
-if (window.ShadyCSS && window.ShadyCSS.nativeCss !== undefined) {
-  nativeCssVariables_ = window.ShadyCSS.nativeCss;
-} else if (window.ShadyCSS) {
-  calcCssVariables(window.ShadyCSS);
-  // reset window variable to let ShadyCSS API take its place
-  window.ShadyCSS = undefined;
-} else {
-  calcCssVariables(window['WebComponents'] && window['WebComponents']['flags']);
-}
-
-// Hack for type error under new type inference which doesn't like that
-// nativeCssVariables is updated in a function and assigns the type
-// `function(): ?` instead of `boolean`.
-export const nativeCssVariables = /** @type {boolean} */(nativeCssVariables_);
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-transformer.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-transformer.js
deleted file mode 100644
index 6803a8b..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-transformer.js
+++ /dev/null
@@ -1,487 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
-import * as StyleUtil from './style-util.js';
-import {nativeShadow} from './style-settings.js';
-
-/* Transforms ShadowDOM styling into ShadyDOM styling
-
-* scoping:
-
-  * elements in scope get scoping selector class="x-foo-scope"
-  * selectors re-written as follows:
-
-    div button -> div.x-foo-scope button.x-foo-scope
-
-* :host -> scopeName
-
-* :host(...) -> scopeName...
-
-* ::slotted(...) -> scopeName > ...
-
-* ...:dir(ltr|rtl) -> [dir="ltr|rtl"] ..., ...[dir="ltr|rtl"]
-
-* :host(:dir[rtl]) -> scopeName:dir(rtl) -> [dir="rtl"] scopeName, scopeName[dir="rtl"]
-
-*/
-const SCOPE_NAME = 'style-scope';
-
-class StyleTransformer {
-  get SCOPE_NAME() {
-    return SCOPE_NAME;
-  }
-  /**
-   * Given a node and scope name, add a scoping class to each node
-   * in the tree. This facilitates transforming css into scoped rules.
-   * @param {!Node} node
-   * @param {string} scope
-   * @param {boolean=} shouldRemoveScope
-   * @deprecated
-   */
-  dom(node, scope, shouldRemoveScope) {
-    const fn = (node) => {
-      this.element(node, scope || '', shouldRemoveScope);
-    };
-    this._transformDom(node, fn);
-  }
-
-  /**
-   * Given a node and scope name, add a scoping class to each node in the tree.
-   * @param {!Node} node
-   * @param {string} scope
-   */
-  domAddScope(node, scope) {
-    const fn = (node) => {
-      this.element(node, scope || '');
-    };
-    this._transformDom(node, fn);
-  }
-
-  /**
-   * @param {!Node} startNode
-   * @param {!function(!Node)} transformer
-   */
-  _transformDom(startNode, transformer) {
-    if (startNode.nodeType === Node.ELEMENT_NODE) {
-      transformer(startNode)
-    }
-    let c$;
-    if (startNode.localName === 'template') {
-      const template = /** @type {!HTMLTemplateElement} */ (startNode);
-      // In case the template is in svg context, fall back to the node
-      // since it won't be an HTMLTemplateElement with a .content property
-      c$ = (template.content || template._content || template).childNodes;
-    } else {
-      c$ = /** @type {!ParentNode} */ (startNode).children ||
-          startNode.childNodes;
-    }
-    if (c$) {
-      for (let i = 0; i < c$.length; i++) {
-        this._transformDom(c$[i], transformer);
-      }
-    }
-  }
-
-  /**
-   * @param {?} element
-   * @param {?} scope
-   * @param {?=} shouldRemoveScope
-   */
-  element(element, scope, shouldRemoveScope) {
-    // note: if using classes, we add both the general 'style-scope' class
-    // as well as the specific scope. This enables easy filtering of all
-    // `style-scope` elements
-    if (scope) {
-      // note: svg on IE does not have classList so fallback to class
-      if (element.classList) {
-        if (shouldRemoveScope) {
-          element.classList.remove(SCOPE_NAME);
-          element.classList.remove(scope);
-        } else {
-          element.classList.add(SCOPE_NAME);
-          element.classList.add(scope);
-        }
-      } else if (element.getAttribute) {
-        let c = element.getAttribute(CLASS);
-        if (shouldRemoveScope) {
-          if (c) {
-            let newValue = c.replace(SCOPE_NAME, '').replace(scope, '');
-            StyleUtil.setElementClassRaw(element, newValue);
-          }
-        } else {
-          let newValue = (c ? c + ' ' : '') + SCOPE_NAME + ' ' + scope;
-          StyleUtil.setElementClassRaw(element, newValue);
-        }
-      }
-    }
-  }
-
-  /**
-   * Given a node, replace the scoping class to each subnode in the tree.
-   * @param {!Node} node
-   * @param {string} oldScope
-   * @param {string} newScope
-   */
-  domReplaceScope(node, oldScope, newScope) {
-    const fn = (node) => {
-      this.element(node, oldScope, true);
-      this.element(node, newScope);
-    };
-    this._transformDom(node, fn);
-  }
-  /**
-   * Given a node, remove the scoping class to each subnode in the tree.
-   * @param {!Node} node
-   * @param {string} oldScope
-   */
-  domRemoveScope(node, oldScope) {
-    const fn = (node) => {
-      this.element(node, oldScope || '', true);
-    };
-    this._transformDom(node, fn);
-  }
-
-  /**
-   * @param {?} element
-   * @param {?} styleRules
-   * @param {?=} callback
-   * @param {string=} cssBuild
-   * @param {string=} cssText
-   * @return {string}
-   */
-  elementStyles(element, styleRules, callback, cssBuild = '', cssText = '') {
-    // no need to shim selectors if settings.useNativeShadow, also
-    // a shady css build will already have transformed selectors
-    // NOTE: This method may be called as part of static or property shimming.
-    // When there is a targeted build it will not be called for static shimming,
-    // but when the property shim is used it is called and should opt out of
-    // static shimming work when a proper build exists.
-    if (cssText === '') {
-      if (nativeShadow || cssBuild === 'shady') {
-        cssText = StyleUtil.toCssText(styleRules, callback);
-      } else {
-        let {is, typeExtension} = StyleUtil.getIsExtends(element);
-        cssText = this.css(styleRules, is, typeExtension, callback) + '\n\n';
-      }
-    }
-    return cssText.trim();
-  }
-
-  // Given a string of cssText and a scoping string (scope), returns
-  // a string of scoped css where each selector is transformed to include
-  // a class created from the scope. ShadowDOM selectors are also transformed
-  // (e.g. :host) to use the scoping selector.
-  css(rules, scope, ext, callback) {
-    let hostScope = this._calcHostScope(scope, ext);
-    scope = this._calcElementScope(scope);
-    let self = this;
-    return StyleUtil.toCssText(rules, function(/** StyleNode */rule) {
-      if (!rule.isScoped) {
-        self.rule(rule, scope, hostScope);
-        rule.isScoped = true;
-      }
-      if (callback) {
-        callback(rule, scope, hostScope);
-      }
-    });
-  }
-
-  _calcElementScope(scope) {
-    if (scope) {
-      return CSS_CLASS_PREFIX + scope;
-    } else {
-      return '';
-    }
-  }
-
-  _calcHostScope(scope, ext) {
-    return ext ? `[is=${scope}]` : scope;
-  }
-
-  rule(rule, scope, hostScope) {
-    this._transformRule(rule, this._transformComplexSelector,
-      scope, hostScope);
-  }
-
-  /**
-   * transforms a css rule to a scoped rule.
-   *
-   * @param {StyleNode} rule
-   * @param {Function} transformer
-   * @param {string=} scope
-   * @param {string=} hostScope
-   */
-  _transformRule(rule, transformer, scope, hostScope) {
-    // NOTE: save transformedSelector for subsequent matching of elements
-    // against selectors (e.g. when calculating style properties)
-    rule['selector'] = rule.transformedSelector =
-      this._transformRuleCss(rule, transformer, scope, hostScope);
-  }
-
-  /**
-   * @param {StyleNode} rule
-   * @param {Function} transformer
-   * @param {string=} scope
-   * @param {string=} hostScope
-   */
-  _transformRuleCss(rule, transformer, scope, hostScope) {
-    let p$ = StyleUtil.splitSelectorList(rule['selector']);
-    // we want to skip transformation of rules that appear in keyframes,
-    // because they are keyframe selectors, not element selectors.
-    if (!StyleUtil.isKeyframesSelector(rule)) {
-      for (let i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) {
-        p$[i] = transformer.call(this, p, scope, hostScope);
-      }
-    }
-    return p$.filter((part) => Boolean(part)).join(COMPLEX_SELECTOR_SEP);
-  }
-
-  /**
-   * @param {string} selector
-   * @return {string}
-   */
-  _twiddleNthPlus(selector) {
-    return selector.replace(NTH, (m, type, inside) => {
-      if (inside.indexOf('+') > -1) {
-        inside = inside.replace(/\+/g, '___');
-      } else if (inside.indexOf('___') > -1) {
-        inside = inside.replace(/___/g, '+');
-      }
-      return `:${type}(${inside})`;
-    });
-  }
-
-  /**
-   * Preserve `:matches()` selectors by replacing them with MATCHES_REPLACMENT
-   * and returning an array of `:matches()` selectors.
-   * Use `_replacesMatchesPseudo` to replace the `:matches()` parts
-   *
-   * @param {string} selector
-   * @return {{selector: string, matches: !Array<string>}}
-   */
-  _preserveMatchesPseudo(selector) {
-    /** @type {!Array<string>} */
-    const matches = [];
-    let match;
-    while ((match = selector.match(MATCHES))) {
-      const start = match.index;
-      const end = StyleUtil.findMatchingParen(selector, start);
-      if (end === -1) {
-        throw new Error(`${match.input} selector missing ')'`)
-      }
-      const part = selector.slice(start, end + 1);
-      selector = selector.replace(part, MATCHES_REPLACEMENT);
-      matches.push(part);
-    }
-    return {selector, matches};
-  }
-
-  /**
-   * Replace MATCHES_REPLACMENT character with the given set of `:matches()`
-   * selectors.
-   *
-   * @param {string} selector
-   * @param {!Array<string>} matches
-   * @return {string}
-   */
-  _replaceMatchesPseudo(selector, matches) {
-    const parts = selector.split(MATCHES_REPLACEMENT);
-    return matches.reduce((acc, cur, idx) => acc + cur + parts[idx + 1], parts[0]);
-  }
-
-/**
- * @param {string} selector
- * @param {string} scope
- * @param {string=} hostScope
- */
-  _transformComplexSelector(selector, scope, hostScope) {
-    let stop = false;
-    selector = selector.trim();
-    // Remove spaces inside of selectors like `:nth-of-type` because it confuses SIMPLE_SELECTOR_SEP
-    let isNth = NTH.test(selector);
-    if (isNth) {
-      selector = selector.replace(NTH, (m, type, inner) => `:${type}(${inner.replace(/\s/g, '')})`)
-      selector = this._twiddleNthPlus(selector);
-    }
-    // Preserve selectors like `:-webkit-any` so that SIMPLE_SELECTOR_SEP does
-    // not get confused by spaces inside the pseudo selector
-    const isMatches = MATCHES.test(selector);
-    /** @type {!Array<string>} */
-    let matches;
-    if (isMatches) {
-      ({selector, matches} = this._preserveMatchesPseudo(selector));
-    }
-    selector = selector.replace(SLOTTED_START, `${HOST} $1`);
-    selector = selector.replace(SIMPLE_SELECTOR_SEP, (m, c, s) => {
-      if (!stop) {
-        let info = this._transformCompoundSelector(s, c, scope, hostScope);
-        stop = stop || info.stop;
-        c = info.combinator;
-        s = info.value;
-      }
-      return c + s;
-    });
-    // replace `:matches()` selectors
-    if (isMatches) {
-      selector = this._replaceMatchesPseudo(selector, matches);
-    }
-    if (isNth) {
-      selector = this._twiddleNthPlus(selector);
-    }
-    selector = selector.replace(DIR_PAREN, (m, before, dir, after) =>
-      `[dir="${dir}"] ${before}${after}, ${before}[dir="${dir}"]${after}`);
-    return selector;
-  }
-
-  _transformCompoundSelector(selector, combinator, scope, hostScope) {
-    // replace :host with host scoping class
-    let slottedIndex = selector.indexOf(SLOTTED);
-    if (selector.indexOf(HOST) >= 0) {
-      selector = this._transformHostSelector(selector, hostScope);
-    // replace other selectors with scoping class
-    } else if (slottedIndex !== 0) {
-      selector = scope ? this._transformSimpleSelector(selector, scope) :
-        selector;
-    }
-    // mark ::slotted() scope jump to replace with descendant selector + arg
-    // also ignore left-side combinator
-    let slotted = false;
-    if (slottedIndex >= 0) {
-      combinator = '';
-      slotted = true;
-    }
-    // process scope jumping selectors up to the scope jump and then stop
-    let stop;
-    if (slotted) {
-      stop = true;
-      if (slotted) {
-        // .zonk ::slotted(.foo) -> .zonk.scope > .foo
-        selector = selector.replace(SLOTTED_PAREN, (m, paren) => ` > ${paren}`);
-      }
-    }
-    return {value: selector, combinator, stop};
-  }
-
-  _transformSimpleSelector(selector, scope) {
-    const attributes = selector.split(/(\[.+?\])/);
-
-    const output = [];
-    for (let i = 0; i < attributes.length; i++) {
-      // Do not attempt to transform any attribute selector content
-      if ((i % 2) === 1) {
-        output.push(attributes[i]);
-      } else {
-        const part = attributes[i];
-
-        if (!(part === '' && i === attributes.length - 1)) {
-          let p$ = part.split(PSEUDO_PREFIX);
-          p$[0] += scope;
-          output.push(p$.join(PSEUDO_PREFIX));
-        }
-      }
-    }
-
-    return output.join('');
-  }
-
-  // :host(...) -> scopeName...
-  _transformHostSelector(selector, hostScope) {
-    let m = selector.match(HOST_PAREN);
-    let paren = m && m[2].trim() || '';
-    if (paren) {
-      if (!paren[0].match(SIMPLE_SELECTOR_PREFIX)) {
-        // paren starts with a type selector
-        let typeSelector = paren.split(SIMPLE_SELECTOR_PREFIX)[0];
-        // if the type selector is our hostScope then avoid pre-pending it
-        if (typeSelector === hostScope) {
-          return paren;
-        // otherwise, this selector should not match in this scope so
-        // output a bogus selector.
-        } else {
-          return SELECTOR_NO_MATCH;
-        }
-      } else {
-        // make sure to do a replace here to catch selectors like:
-        // `:host(.foo)::before`
-        return selector.replace(HOST_PAREN, function(m, host, paren) {
-          return hostScope + paren;
-        });
-      }
-    // if no paren, do a straight :host replacement.
-    // TODO(sorvell): this should not strictly be necessary but
-    // it's needed to maintain support for `:host[foo]` type selectors
-    // which have been improperly used under Shady DOM. This should be
-    // deprecated.
-    } else {
-      return selector.replace(HOST, hostScope);
-    }
-  }
-
-  /**
-   * @param {StyleNode} rule
-   */
-  documentRule(rule) {
-    // reset selector in case this is redone.
-    rule['selector'] = rule['parsedSelector'];
-    this.normalizeRootSelector(rule);
-    this._transformRule(rule, this._transformDocumentSelector);
-  }
-
-  /**
-   * @param {StyleNode} rule
-   */
-  normalizeRootSelector(rule) {
-    if (rule['selector'] === ROOT) {
-      rule['selector'] = 'html';
-    }
-  }
-
-/**
- * @param {string} selector
- */
-  _transformDocumentSelector(selector) {
-    if (selector.match(HOST)) {
-      // remove ':host' type selectors in document rules
-      return '';
-    } else if (selector.match(SLOTTED)) {
-      return this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR)
-    } else {
-      return this._transformSimpleSelector(selector.trim(), SCOPE_DOC_SELECTOR);
-    }
-  }
-}
-
-const NTH = /:(nth[-\w]+)\(([^)]+)\)/;
-const SCOPE_DOC_SELECTOR = `:not(.${SCOPE_NAME})`;
-const COMPLEX_SELECTOR_SEP = ',';
-const SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g;
-const SIMPLE_SELECTOR_PREFIX = /[[.:#*]/;
-const HOST = ':host';
-const ROOT = ':root';
-const SLOTTED = '::slotted';
-const SLOTTED_START = new RegExp(`^(${SLOTTED})`);
-// NOTE: this supports 1 nested () pair for things like
-// :host(:not([selected]), more general support requires
-// parsing which seems like overkill
-const HOST_PAREN = /(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/;
-// similar to HOST_PAREN
-const SLOTTED_PAREN = /(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/;
-const DIR_PAREN = /(.*):dir\((?:(ltr|rtl))\)(.*)/;
-const CSS_CLASS_PREFIX = '.';
-const PSEUDO_PREFIX = ':';
-const CLASS = 'class';
-const SELECTOR_NO_MATCH = 'should_not_match';
-const MATCHES = /:(?:matches|any|-(?:webkit|moz)-any)/;
-const MATCHES_REPLACEMENT = '\u{e000}';
-
-export default new StyleTransformer()
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-util.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-util.js
deleted file mode 100644
index 1e58c5b..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-util.js
+++ /dev/null
@@ -1,414 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-import {nativeShadow, nativeCssVariables, cssBuild} from './style-settings.js';
-import {parse, stringify, types, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
-import {MEDIA_MATCH} from './common-regex.js';
-import {processUnscopedStyle, isUnscopedStyle} from './unscoped-style-handler.js';
-
-/**
- * @param {string|StyleNode} rules
- * @param {function(StyleNode)=} callback
- * @return {string}
- */
-export function toCssText (rules, callback) {
-  if (!rules) {
-    return '';
-  }
-  if (typeof rules === 'string') {
-    rules = parse(rules);
-  }
-  if (callback) {
-    forEachRule(rules, callback);
-  }
-  return stringify(rules, nativeCssVariables);
-}
-
-/**
- * @param {HTMLStyleElement} style
- * @return {StyleNode}
- */
-export function rulesForStyle(style) {
-  if (!style['__cssRules'] && style.textContent) {
-    style['__cssRules'] = parse(style.textContent);
-  }
-  return style['__cssRules'] || null;
-}
-
-// Tests if a rule is a keyframes selector, which looks almost exactly
-// like a normal selector but is not (it has nothing to do with scoping
-// for example).
-/**
- * @param {StyleNode} rule
- * @return {boolean}
- */
-export function isKeyframesSelector(rule) {
-  return Boolean(rule['parent']) &&
-  rule['parent']['type'] === types.KEYFRAMES_RULE;
-}
-
-/**
- * @param {StyleNode} node
- * @param {Function=} styleRuleCallback
- * @param {Function=} keyframesRuleCallback
- * @param {boolean=} onlyActiveRules
- */
-export function forEachRule(node, styleRuleCallback, keyframesRuleCallback, onlyActiveRules) {
-  if (!node) {
-    return;
-  }
-  let skipRules = false;
-  let type = node['type'];
-  if (onlyActiveRules) {
-    if (type === types.MEDIA_RULE) {
-      let matchMedia = node['selector'].match(MEDIA_MATCH);
-      if (matchMedia) {
-        // if rule is a non matching @media rule, skip subrules
-        if (!window.matchMedia(matchMedia[1]).matches) {
-          skipRules = true;
-        }
-      }
-    }
-  }
-  if (type === types.STYLE_RULE) {
-    styleRuleCallback(node);
-  } else if (keyframesRuleCallback &&
-    type === types.KEYFRAMES_RULE) {
-    keyframesRuleCallback(node);
-  } else if (type === types.MIXIN_RULE) {
-    skipRules = true;
-  }
-  let r$ = node['rules'];
-  if (r$ && !skipRules) {
-    for (let i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {
-      forEachRule(r, styleRuleCallback, keyframesRuleCallback, onlyActiveRules);
-    }
-  }
-}
-
-// add a string of cssText to the document.
-/**
- * @param {string} cssText
- * @param {string} moniker
- * @param {Node} target
- * @param {Node} contextNode
- * @return {HTMLStyleElement}
- */
-export function applyCss(cssText, moniker, target, contextNode) {
-  let style = createScopeStyle(cssText, moniker);
-  applyStyle(style, target, contextNode);
-  return style;
-}
-
-/**
- * @param {string} cssText
- * @param {string} moniker
- * @return {HTMLStyleElement}
- */
-export function createScopeStyle(cssText, moniker) {
-  let style = /** @type {HTMLStyleElement} */(document.createElement('style'));
-  if (moniker) {
-    style.setAttribute('scope', moniker);
-  }
-  style.textContent = cssText;
-  return style;
-}
-
-/**
- * Track the position of the last added style for placing placeholders
- * @type {Node}
- */
-let lastHeadApplyNode = null;
-
-// insert a comment node as a styling position placeholder.
-/**
- * @param {string} moniker
- * @return {!Comment}
- */
-export function applyStylePlaceHolder(moniker) {
-  let placeHolder = document.createComment(' Shady DOM styles for ' +
-    moniker + ' ');
-  let after = lastHeadApplyNode ?
-    lastHeadApplyNode['nextSibling'] : null;
-  let scope = document.head;
-  scope.insertBefore(placeHolder, after || scope.firstChild);
-  lastHeadApplyNode = placeHolder;
-  return placeHolder;
-}
-
-/**
- * @param {HTMLStyleElement} style
- * @param {?Node} target
- * @param {?Node} contextNode
- */
-export function applyStyle(style, target, contextNode) {
-  target = target || document.head;
-  let after = (contextNode && contextNode.nextSibling) ||
-    target.firstChild;
-  target.insertBefore(style, after);
-  if (!lastHeadApplyNode) {
-    lastHeadApplyNode = style;
-  } else {
-    // only update lastHeadApplyNode if the new style is inserted after the old lastHeadApplyNode
-    let position = style.compareDocumentPosition(lastHeadApplyNode);
-    if (position === Node.DOCUMENT_POSITION_PRECEDING) {
-      lastHeadApplyNode = style;
-    }
-  }
-}
-
-/**
- * @param {string} buildType
- * @return {boolean}
- */
-export function isTargetedBuild(buildType) {
-  return nativeShadow ? buildType === 'shadow' : buildType === 'shady';
-}
-
-/**
- * Walk from text[start] matching parens and
- * returns position of the outer end paren
- * @param {string} text
- * @param {number} start
- * @return {number}
- */
-export function findMatchingParen(text, start) {
-  let level = 0;
-  for (let i=start, l=text.length; i < l; i++) {
-    if (text[i] === '(') {
-      level++;
-    } else if (text[i] === ')') {
-      if (--level === 0) {
-        return i;
-      }
-    }
-  }
-  return -1;
-}
-
-/**
- * @param {string} str
- * @param {function(string, string, string, string)} callback
- */
-export function processVariableAndFallback(str, callback) {
-  // find 'var('
-  let start = str.indexOf('var(');
-  if (start === -1) {
-    // no var?, everything is prefix
-    return callback(str, '', '', '');
-  }
-  //${prefix}var(${inner})${suffix}
-  let end = findMatchingParen(str, start + 3);
-  let inner = str.substring(start + 4, end);
-  let prefix = str.substring(0, start);
-  // suffix may have other variables
-  let suffix = processVariableAndFallback(str.substring(end + 1), callback);
-  let comma = inner.indexOf(',');
-  // value and fallback args should be trimmed to match in property lookup
-  if (comma === -1) {
-    // variable, no fallback
-    return callback(prefix, inner.trim(), '', suffix);
-  }
-  // var(${value},${fallback})
-  let value = inner.substring(0, comma).trim();
-  let fallback = inner.substring(comma + 1).trim();
-  return callback(prefix, value, fallback, suffix);
-}
-
-/**
- * @param {Element} element
- * @param {string} value
- */
-export function setElementClassRaw(element, value) {
-  // use native setAttribute provided by ShadyDOM when setAttribute is patched
-  if (nativeShadow) {
-    element.setAttribute('class', value);
-  } else {
-    window['ShadyDOM']['nativeMethods']['setAttribute'].call(element, 'class', value);
-  }
-}
-
-/**
- * @type {function(*):*}
- */
-export const wrap = window['ShadyDOM'] && window['ShadyDOM']['wrap'] || ((node) => node);
-
-/**
- * @param {Element | {is: string, extends: string}} element
- * @return {{is: string, typeExtension: string}}
- */
-export function getIsExtends(element) {
-  let localName = element['localName'];
-  let is = '', typeExtension = '';
-  /*
-  NOTE: technically, this can be wrong for certain svg elements
-  with `-` in the name like `<font-face>`
-  */
-  if (localName) {
-    if (localName.indexOf('-') > -1) {
-      is = localName;
-    } else {
-      typeExtension = localName;
-      is = (element.getAttribute && element.getAttribute('is')) || '';
-    }
-  } else {
-    is = /** @type {?} */(element).is;
-    typeExtension = /** @type {?} */(element).extends;
-  }
-  return {is, typeExtension};
-}
-
-/**
- * @param {Element|DocumentFragment} element
- * @return {string}
- */
-export function gatherStyleText(element) {
-  /** @type {!Array<string>} */
-  const styleTextParts = [];
-  const styles = /** @type {!NodeList<!HTMLStyleElement>} */(element.querySelectorAll('style'));
-  for (let i = 0; i < styles.length; i++) {
-    const style = styles[i];
-    if (isUnscopedStyle(style)) {
-      if (!nativeShadow) {
-        processUnscopedStyle(style);
-        style.parentNode.removeChild(style);
-      }
-    } else {
-      styleTextParts.push(style.textContent);
-      style.parentNode.removeChild(style);
-    }
-  }
-  return styleTextParts.join('').trim();
-}
-
-/**
- * Split a selector separated by commas into an array in a smart way
- * @param {string} selector
- * @return {!Array<string>}
- */
-export function splitSelectorList(selector) {
-  const parts = [];
-  let part = '';
-  for (let i = 0; i >= 0 && i < selector.length; i++) {
-    // A selector with parentheses will be one complete part
-    if (selector[i] === '(') {
-      // find the matching paren
-      const end = findMatchingParen(selector, i);
-      // push the paren block into the part
-      part += selector.slice(i, end + 1);
-      // move the index to after the paren block
-      i = end;
-    } else if (selector[i] === ',') {
-      parts.push(part);
-      part = '';
-    } else {
-      part += selector[i];
-    }
-  }
-  // catch any pieces after the last comma
-  if (part) {
-    parts.push(part);
-  }
-  return parts;
-}
-
-const CSS_BUILD_ATTR = 'css-build';
-
-/**
- * Return the polymer-css-build "build type" applied to this element
- *
- * @param {!HTMLElement} element
- * @return {string} Can be "", "shady", or "shadow"
- */
-export function getCssBuild(element) {
-  if (cssBuild !== undefined) {
-    return /** @type {string} */(cssBuild);
-  }
-  if (element.__cssBuild === undefined) {
-    // try attribute first, as it is the common case
-    const attrValue = element.getAttribute(CSS_BUILD_ATTR);
-    if (attrValue) {
-      element.__cssBuild = attrValue;
-    } else {
-      const buildComment = getBuildComment(element);
-      if (buildComment !== '') {
-        // remove build comment so it is not needlessly copied into every element instance
-        removeBuildComment(element);
-      }
-      element.__cssBuild = buildComment;
-    }
-  }
-  return element.__cssBuild || '';
-}
-
-/**
- * Check if the given element, either a <template> or <style>, has been processed
- * by polymer-css-build.
- *
- * If so, then we can make a number of optimizations:
- * - polymer-css-build will decompose mixins into individual CSS Custom Properties,
- * so the ApplyShim can be skipped entirely.
- * - Under native ShadowDOM, the style text can just be copied into each instance
- * without modification
- * - If the build is "shady" and ShadyDOM is in use, the styling does not need
- * scoping beyond the shimming of CSS Custom Properties
- *
- * @param {!HTMLElement} element
- * @return {boolean}
- */
-export function elementHasBuiltCss(element) {
-  return getCssBuild(element) !== '';
-}
-
-/**
- * For templates made with tagged template literals, polymer-css-build will
- * insert a comment of the form `<!--css-build:shadow-->`
- *
- * @param {!HTMLElement} element
- * @return {string}
- */
-export function getBuildComment(element) {
-  const buildComment = element.localName === 'template' ?
-      /** @type {!HTMLTemplateElement} */ (element).content.firstChild :
-      element.firstChild;
-  if (buildComment instanceof Comment) {
-    const commentParts = buildComment.textContent.trim().split(':');
-    if (commentParts[0] === CSS_BUILD_ATTR) {
-      return commentParts[1];
-    }
-  }
-  return '';
-}
-
-/**
- * Check if the css build status is optimal, and do no unneeded work.
- *
- * @param {string=} cssBuild CSS build status
- * @return {boolean} css build is optimal or not
- */
-export function isOptimalCssBuild(cssBuild = '') {
-  // CSS custom property shim always requires work
-  if (cssBuild === '' || !nativeCssVariables) {
-    return false;
-  }
-  return nativeShadow ? cssBuild === 'shadow' : cssBuild === 'shady';
-}
-
-/**
- * @param {!HTMLElement} element
- */
-function removeBuildComment(element) {
-  const buildComment = element.localName === 'template' ?
-      /** @type {!HTMLTemplateElement} */ (element).content.firstChild :
-      element.firstChild;
-  buildComment.parentNode.removeChild(buildComment);
-}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/template-map.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/template-map.js
deleted file mode 100644
index d3eb840..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/template-map.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-/**
- * @const {!Object<string, !HTMLTemplateElement>}
- */
-const templateMap = {};
-export default templateMap;
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/unscoped-style-handler.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/unscoped-style-handler.js
deleted file mode 100644
index 95e994f..0000000
--- a/third_party/polymer/v3_0/components-chromium/shadycss/src/unscoped-style-handler.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-*/
-
-'use strict';
-
-/** @type {!Set<string>} */
-const styleTextSet = new Set();
-
-export const scopingAttribute = 'shady-unscoped';
-
-/**
- * Add a specifically-marked style to the document directly, and only one copy of that style.
- *
- * @param {!HTMLStyleElement} style
- * @return {undefined}
- */
-export function processUnscopedStyle(style) {
-  const text = style.textContent;
-  if (!styleTextSet.has(text)) {
-    styleTextSet.add(text);
-    const newStyle = style.cloneNode(true);
-    document.head.appendChild(newStyle);
-  }
-}
-
-/**
- * Check if a style is supposed to be unscoped
- * @param {!HTMLStyleElement} style
- * @return {boolean} true if the style has the unscoping attribute
- */
-export function isUnscopedStyle(style) {
-  return style.hasAttribute(scopingAttribute);
-}
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/minify_polymer.py b/third_party/polymer/v3_0/minify_polymer.py
new file mode 100644
index 0000000..462b0bc9
--- /dev/null
+++ b/third_party/polymer/v3_0/minify_polymer.py
@@ -0,0 +1,75 @@
+# Copyrigh 2019 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.
+
+"""Minifies Polymer 3, since it does not come already minified from NPM."""
+
+import os
+import shutil
+import sys
+import tempfile
+
+_HERE_PATH = os.path.dirname(__file__)
+_SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..'))
+sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node'))
+import node
+import node_modules
+
+
+def main():
+  polymer_dir = os.path.join(_HERE_PATH, 'components-chromium', 'polymer')
+
+  # Final JS bundle.
+  polymer_js = os.path.join(polymer_dir, 'polymer_bundled.min.js')
+
+  # Copy the top-level Polymer file that holds all dependencies. This file is
+  # not distributed via NPM, it only exists within third_party/polymer
+  # repository.
+  shutil.copy(
+      os.path.join(polymer_dir, '..', '..', 'polymer.js'), polymer_dir);
+
+  # Move the entire checkout to a temp location.
+  tmp_dir = os.path.join(_HERE_PATH, 'components-chromium', 'polymer_temp')
+  if os.path.exists(tmp_dir):
+    shutil.rmtree(tmp_dir)
+  shutil.move(polymer_dir, tmp_dir)
+
+  tmp_out_dir = os.path.join(tmp_dir, 'out')
+  os.makedirs(tmp_out_dir)
+
+  try:
+    # Combine everything to a single JS bundle file.
+    bundled_js = os.path.join(tmp_out_dir, 'polymer_bundled.js')
+    path_to_rollup = os.path.join('node_modules', 'rollup', 'bin', 'rollup');
+
+    node.RunNode([
+        path_to_rollup,
+        # See https://github.com/rollup/rollup/issues/1955
+        '--silent',
+        '--format', 'esm',
+        '--input', os.path.join(tmp_dir, 'polymer.js'),
+        '--file', bundled_js,
+    ])
+
+    # Minify the JS bundle.
+    minified_js = os.path.join(tmp_out_dir, 'polymer_bundled.min.js')
+    node.RunNode([
+        node_modules.PathToUglify(), bundled_js,
+        # TODO(dpapad): Figure out a way to deduplicate LICENSE headers.
+        #'--comments', '"/Copyright|license|LICENSE/"',
+        '--output', minified_js])
+
+    # Copy generated JS bundle back to the original location.
+    os.makedirs(polymer_dir)
+    shutil.move(minified_js, polymer_js)
+
+    # Copy a few more files.
+    shutil.move(os.path.join(tmp_dir, 'LICENSE.txt'), polymer_dir)
+  finally:
+    # Delete component-chromium/shadycss since it ends up in the bundle.
+    shutil.rmtree(os.path.join(_HERE_PATH, 'components-chromium', 'shadycss'))
+    shutil.rmtree(tmp_dir)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/polymer/v3_0/package-lock.json b/third_party/polymer/v3_0/package-lock.json
index 8b8fa5a..102529e 100644
--- a/third_party/polymer/v3_0/package-lock.json
+++ b/third_party/polymer/v3_0/package-lock.json
@@ -353,10 +353,35 @@
         "@webcomponents/shadycss": "1.9.1"
       }
     },
+    "@types/estree": {
+      "version": "0.0.39",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+      "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
+    },
+    "@types/node": {
+      "version": "12.0.3",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.3.tgz",
+      "integrity": "sha512-zkOxCS/fA+3SsdA+9Yun0iANxzhQRiNwTvJSr6N95JhuJ/x27z9G2URx1Jpt3zYFfCGUXZGL5UDxt5eyLE7wgw=="
+    },
     "@webcomponents/shadycss": {
       "version": "1.9.1",
       "resolved": "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.9.1.tgz",
       "integrity": "sha512-IaZOnWOKXHghqk/WfPNDRIgDBi3RsVPY2IFAw6tYiL9UBGvQRy5R6uC+Fk7qTZsReTJ0xh5MTT8yAcb3MUR4mQ=="
+    },
+    "acorn": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
+      "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA=="
+    },
+    "rollup": {
+      "version": "1.12.4",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.12.4.tgz",
+      "integrity": "sha512-sHg0F05oTMJzM592MWU8irsPx8LIFMKSCnEkcp6vp/gnj+oJ9GJEBW9hl8jUqy2L6Q2uUxFzPgvoExLbfuSODA==",
+      "requires": {
+        "@types/estree": "0.0.39",
+        "@types/node": "12.0.3",
+        "acorn": "6.1.1"
+      }
     }
   }
 }
diff --git a/third_party/polymer/v3_0/package.json b/third_party/polymer/v3_0/package.json
index 430cfa5..866685a 100644
--- a/third_party/polymer/v3_0/package.json
+++ b/third_party/polymer/v3_0/package.json
@@ -3,41 +3,42 @@
   "version": "1.0.0",
   "author": "dpapad@chromium.org",
   "dependencies": {
-    "@polymer/font-roboto": "^3.0.2",
-    "@polymer/iron-a11y-announcer": "^3.0.2",
-    "@polymer/iron-a11y-keys": "^3.0.1",
-    "@polymer/iron-a11y-keys-behavior": "^3.0.1",
-    "@polymer/iron-behaviors": "^3.0.1",
-    "@polymer/iron-collapse": "^3.0.1",
-    "@polymer/iron-dropdown": "^3.0.1",
-    "@polymer/iron-fit-behavior": "^3.0.1",
-    "@polymer/iron-flex-layout": "^3.0.1",
-    "@polymer/iron-icon": "^3.0.1",
-    "@polymer/iron-iconset-svg": "^3.0.1",
-    "@polymer/iron-input": "^3.0.1",
-    "@polymer/iron-list": "^3.0.2",
-    "@polymer/iron-location": "^3.0.1",
-    "@polymer/iron-media-query": "^3.0.1",
-    "@polymer/iron-meta": "^3.0.1",
-    "@polymer/iron-overlay-behavior": "^3.0.2",
-    "@polymer/iron-pages": "^3.0.1",
-    "@polymer/iron-range-behavior": "^3.0.1",
-    "@polymer/iron-resizable-behavior": "^3.0.1",
-    "@polymer/iron-scroll-target-behavior": "^3.0.1",
-    "@polymer/iron-scroll-threshold": "^3.0.1",
-    "@polymer/iron-selector": "^3.0.1",
-    "@polymer/iron-test-helpers": "^3.0.1",
-    "@polymer/iron-validatable-behavior": "^3.0.1",
-    "@polymer/neon-animation": "^3.0.1",
-    "@polymer/paper-behaviors": "^3.0.1",
-    "@polymer/paper-button": "^3.0.1",
-    "@polymer/paper-input": "^3.0.2",
-    "@polymer/paper-progress": "^3.0.1",
-    "@polymer/paper-ripple": "^3.0.1",
-    "@polymer/paper-spinner": "^3.0.2",
-    "@polymer/paper-styles": "^3.0.1",
-    "@polymer/paper-tooltip": "^3.0.1",
-    "@polymer/polymer": "^3.2.0",
-    "@webcomponents/shadycss": "^1.9.1"
+    "@polymer/font-roboto": "3.0.2",
+    "@polymer/iron-a11y-announcer": "3.0.2",
+    "@polymer/iron-a11y-keys": "3.0.1",
+    "@polymer/iron-a11y-keys-behavior": "3.0.1",
+    "@polymer/iron-behaviors": "3.0.1",
+    "@polymer/iron-collapse": "3.0.1",
+    "@polymer/iron-dropdown": "3.0.1",
+    "@polymer/iron-fit-behavior": "3.0.1",
+    "@polymer/iron-flex-layout": "3.0.1",
+    "@polymer/iron-icon": "3.0.1",
+    "@polymer/iron-iconset-svg": "3.0.1",
+    "@polymer/iron-input": "3.0.1",
+    "@polymer/iron-list": "3.0.2",
+    "@polymer/iron-location": "3.0.1",
+    "@polymer/iron-media-query": "3.0.1",
+    "@polymer/iron-meta": "3.0.1",
+    "@polymer/iron-overlay-behavior": "3.0.2",
+    "@polymer/iron-pages": "3.0.1",
+    "@polymer/iron-range-behavior": "3.0.1",
+    "@polymer/iron-resizable-behavior": "3.0.1",
+    "@polymer/iron-scroll-target-behavior": "3.0.1",
+    "@polymer/iron-scroll-threshold": "3.0.1",
+    "@polymer/iron-selector": "3.0.1",
+    "@polymer/iron-test-helpers": "3.0.1",
+    "@polymer/iron-validatable-behavior": "3.0.1",
+    "@polymer/neon-animation": "3.0.1",
+    "@polymer/paper-behaviors": "3.0.1",
+    "@polymer/paper-button": "3.0.1",
+    "@polymer/paper-input": "3.0.2",
+    "@polymer/paper-progress": "3.0.1",
+    "@polymer/paper-ripple": "3.0.1",
+    "@polymer/paper-spinner": "3.0.2",
+    "@polymer/paper-styles": "3.0.1",
+    "@polymer/paper-tooltip": "3.0.1",
+    "@polymer/polymer": "3.2.0",
+    "@webcomponents/shadycss": "1.9.1",
+    "rollup": "1.12.4"
   }
 }
diff --git a/third_party/polymer/v3_0/polymer.js b/third_party/polymer/v3_0/polymer.js
new file mode 100644
index 0000000..4bf68e2
--- /dev/null
+++ b/third_party/polymer/v3_0/polymer.js
@@ -0,0 +1,25 @@
+/**
+@license
+Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+*/
+
+export {animationFrame, idlePeriod, microTask} from './lib/utils/async.js';
+import * as gestures from './lib/utils/gestures.js';
+export {gestures};
+export {Base} from './polymer-legacy.js';
+export {dashToCamelCase} from './lib/utils/case-map.js';
+export {Debouncer, enqueueDebouncer} from './lib/utils/debounce.js';
+export {dom, flush} from './lib/legacy/polymer.dom.js';
+export {html} from './lib/utils/html-tag.js';
+export {matches, translate} from './lib/utils/path.js';
+export {OptionalMutableDataBehavior} from './lib/legacy/mutable-data-behavior.js';
+export {Polymer} from './lib/legacy/polymer-fn.js';
+export {PolymerElement} from './polymer-element.js';
+export {TemplateInstanceBase} from './lib/utils/templatize.js';
+export {Templatizer} from './lib/legacy/templatizer-behavior.js';
+export {useShadow} from './lib/utils/settings.js';
diff --git a/third_party/polymer/v3_0/reproduce.sh b/third_party/polymer/v3_0/reproduce.sh
index bbf9fd9..74746f2 100755
--- a/third_party/polymer/v3_0/reproduce.sh
+++ b/third_party/polymer/v3_0/reproduce.sh
@@ -48,6 +48,14 @@
 # Apply additional chrome specific patches.
 patch -p1 --forward -r - < chromium.patch
 
+echo 'Minifying Polymer 3, since it comes non-minified from NPM.'
+python minify_polymer.py
+
+echo 'Updating paper/iron elements to point to the minified file.'
+# Replace all paths that point to within polymer/ to point to the bundle.
+find components-chromium/ -name '*.js' -exec sed -i \
+  's/\/polymer\/[a-zA-Z\/\.-]\+/\/polymer\/polymer_bundled.min.js/' {} +
+
 new=$(git status --porcelain components-chromium | grep '^??' | \
       cut -d' ' -f2 | egrep '\.(js|css)$' || true)
 
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/core/renderer.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/core/renderer.js
index 04ebf2d..c4d7c34 100644
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/core/renderer.js
+++ b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/core/renderer.js
@@ -557,7 +557,7 @@
     if (buffer._buffer) {
       let gl = this._gl;
       gl.bindBuffer(buffer._target, buffer._buffer);
-      if (offset == 0 && buffer._length == data.byteLength) {
+      if (offset == 0 && buffer._length <= data.byteLength) {
         gl.bufferData(buffer._target, data, buffer._usage);
       } else {
         gl.bufferSubData(buffer._target, offset, data);
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/nodes/plane-node.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/nodes/plane-node.js
index 95d59e4..ad28946 100644
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/nodes/plane-node.js
+++ b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/nodes/plane-node.js
@@ -18,8 +18,9 @@
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 import {Material, RENDER_ORDER} from '../core/material.js';
+import {Primitive, PrimitiveAttribute} from '../core/primitive.js';
 import {Node} from '../core/node.js';
-import {GeometryBuilderBase} from '../geometry/primitive-stream.js';
+import {vec3} from '../math/gl-matrix.js';
 
 const GL = WebGLRenderingContext; // For enums
 
@@ -45,7 +46,6 @@
   get vertexSource() {
     return `
     attribute vec3 POSITION;
-    attribute vec3 NORMAL;
 
     varying vec3 vLight;
 
@@ -54,7 +54,7 @@
     const vec3 lightColor = vec3(0.75, 0.75, 0.75);
 
     vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {
-      vec3 normalRotated = vec3(model * vec4(NORMAL, 0.0));
+      vec3 normalRotated = vec3(model * vec4(0.0, 0.0, 1.0, 0.0));
       float lightFactor = max(dot(normalize(lightDir), normalRotated), 0.0);
       vLight = ambientColor + (lightColor * lightFactor);
       return proj * view * model * vec4(POSITION, 1.0);
@@ -87,28 +87,85 @@
     this.baseColor = options.baseColor;
     this.polygon = options.polygon;
 
+    // ring buffer containing last 3 plane primitives (meshes)
+    this.primitives = [null, null, null];
+    this.primitiveIndex = -1;
+
     this._material = new PlaneMaterial({baseColor : options.baseColor});
 
     this._renderer = null;
   }
 
   createPlanePrimitive(polygon) {
-    // TODO: create new builder class for planes
-    let planeBuilder = new GeometryBuilderBase();
+    let vertices = [];
+    let indices = [];
+    let min = null;
+    let max = null;
 
-    planeBuilder.primitiveStream.startGeometry();
-    let numVertices = polygon.length;
-    let firstVertex = planeBuilder.primitiveStream.nextVertexIndex;
+    // first, collect all polygon vertices
     polygon.forEach(vertex => {
-      planeBuilder.primitiveStream.pushVertex(vertex.x, vertex.y, vertex.z);
+      vertices.push(vertex.x, vertex.y, vertex.z);
+
+      if(min) {
+        min[0] = Math.min(min[0], vertex.x);
+        min[1] = Math.min(min[1], vertex.y);
+        min[2] = Math.min(min[2], vertex.z);
+      } else {
+        min = vec3.fromValues(vertex.x, vertex.y, vertex.z);
+      }
+
+      if(max) {
+        max[0] = Math.min(min[0], vertex.x);
+        max[1] = Math.min(max[1], vertex.y);
+        max[2] = Math.min(max[2], vertex.z);
+      } else {
+        max = vec3.fromValues(vertex.x, vertex.y, vertex.z);
+      }
     });
 
-    for(let i = 0; i < numVertices - 2; i++) {
-      planeBuilder.primitiveStream.pushTriangle(firstVertex, firstVertex + i + 1, firstVertex + i + 2);
+    // then indices
+    for(let i = 0; i < polygon.length - 2; i++) {
+      indices.push(0, i + 1, i + 2);
     }
-    planeBuilder.primitiveStream.endGeometry();
 
-    return planeBuilder.finishPrimitive(this._renderer);
+    let newPrimitiveIndex = (this.primitiveIndex + 1) % this.primitives.length;
+    if(this.primitives[newPrimitiveIndex]) {
+      // update
+      let oldPrimitive = this.primitives[newPrimitiveIndex];
+
+      this._renderer.updateRenderBuffer(oldPrimitive.attributes[0].buffer, new Float32Array(vertices));
+      this._renderer.updateRenderBuffer(oldPrimitive.indexBuffer, new Uint16Array(indices));
+
+      // attribs are still set, no need to re-set them
+
+      // index buffer is still set, no need to re-set it
+      oldPrimitive.setBounds(min, max);
+      oldPrimitive.elementCount = indices.length;
+    } else {
+      // add
+      let vertexBuffer = this._renderer.createRenderBuffer(
+        GL.ARRAY_BUFFER, new Float32Array(vertices));
+      let indexBuffer = this._renderer.createRenderBuffer(
+        GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));
+
+      let position_attribute = new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 12, 0);
+
+      let newPrimitive = new Primitive([position_attribute], indices.length);
+      newPrimitive.setIndexBuffer(indexBuffer);
+      newPrimitive.setBounds(min, max);
+
+      this.primitives[newPrimitiveIndex] = newPrimitive;
+    }
+
+    this.primitiveIndex = newPrimitiveIndex;
+  }
+
+  get primitive() {
+    if(!this.primitives[this.primitiveIndex]) {
+      throw new Error(`Primitive is not set! Call createPlanePrimitive first!`);
+    }
+
+    return this.primitives[this.primitiveIndex];
   }
 
   onRendererChanged(renderer) {
@@ -117,8 +174,9 @@
 
     this._renderer = renderer;
 
-    this.planeNode = this._renderer.createRenderPrimitive(
-      this.createPlanePrimitive(this.polygon), this._material);
+    this.createPlanePrimitive(this.polygon);
+
+    this.planeNode = this._renderer.createRenderPrimitive(this.primitive, this._material);
     this.addRenderPrimitive(this.planeNode);
 
     this.polygon = null;
@@ -130,9 +188,16 @@
     if(this.polygon)
       throw new Error(`Polygon is set on a plane where it shouldn't be!`);
 
-    let updatedPrimitive = this.createPlanePrimitive(polygon);
+    this.createPlanePrimitive(polygon);
 
-    this.planeNode.setPrimitive(updatedPrimitive);
+    // eagerly clean up render primitive's VAO
+    if(this.planeNode._vao) {
+      this._renderer._vaoExt.deleteVertexArrayOES(this.planeNode._vao);
+      this.planeNode._vao = null;
+    }
+
+    this.planeNode.setPrimitive(this.primitive);
+
     return this.planeNode.waitForComplete();
   }
 }
diff --git a/tools/binary_size/diagnose_bloat.py b/tools/binary_size/diagnose_bloat.py
index 7727039..c4cb3a3cb 100755
--- a/tools/binary_size/diagnose_bloat.py
+++ b/tools/binary_size/diagnose_bloat.py
@@ -268,14 +268,11 @@
     if self.IsLinux():
       return 'chrome'
     if 'monochrome' in self.target or 'trichrome' in self.target:
-      ret = 'lib.unstripped/libmonochrome_base.so'
+      ret = 'lib.unstripped/libmonochrome.so'
     elif 'webview' in self.target:
       ret = 'lib.unstripped/libwebviewchromium.so'
     else:
-      ret = 'lib.unstripped/libchrome_base.so'
-    # Maintain support for measuring non-bundle apks.
-    if not self.is_bundle:
-      ret = ret.replace('_base', '')
+      ret = 'lib.unstripped/libchrome.so'
     return ret
 
   @property
diff --git a/tools/chrome_proxy/webdriver/https_previews.py b/tools/chrome_proxy/webdriver/https_previews.py
index 5555ed4b..167ae76f 100644
--- a/tools/chrome_proxy/webdriver/https_previews.py
+++ b/tools/chrome_proxy/webdriver/https_previews.py
@@ -72,9 +72,9 @@
         self.assertRegexpMatches(response.url, LITEPAGES_REGEXP)
         self.assertEqual(200, response.status)
         lite_page_responses += 1
-      if 'image/' in content_type:
-        self.assertRegexpMatches(response.url, LITEPAGES_REGEXP)
-        self.assertEqual(200, response.status)
+      if ('image/' in content_type
+          and re.match(LITEPAGES_REGEXP, response.url)
+          and 200 == response.status):
         image_responses += 1
 
     self.assertEqual(1, lite_page_responses)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 07299ed..10b5908 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3293,6 +3293,7 @@
   <int value="2" label="Drag and drop"/>
   <int value="3" label="Double click"/>
   <int value="4" label="Pause"/>
+  <int value="5" label="Scroll"/>
 </enum>
 
 <enum name="AutocompleteActionPredictorAction">
@@ -25019,6 +25020,7 @@
   </summary>
   <int value="0" label="All frames have distinct SiteInstances"/>
   <int value="1" label="Some frames have the same SiteInstance"/>
+  <int value="2" label="Only one frame exists"/>
 </enum>
 
 <enum name="FrameVisibility">
@@ -33301,6 +33303,7 @@
   <int value="-1811394154" label="disable-webrtc-hw-vp8-encoding"/>
   <int value="-1810294310" label="AndroidPaymentApps:enabled"/>
   <int value="-1809891158" label="WebAuthenticationCable:enabled"/>
+  <int value="-1809865209" label="KernelnextVMs:enabled"/>
   <int value="-1809835803" label="LayoutNG:disabled"/>
   <int value="-1808576075" label="SystemTrayUnified:enabled"/>
   <int value="-1808477331" label="MidiManagerCros:disabled"/>
@@ -34517,6 +34520,7 @@
   <int value="-11260186" label="enable-offline-pages-as-saved-pages"/>
   <int value="-10709540"
       label="OmniboxUIExperimentHideSuggestionUrlScheme:enabled"/>
+  <int value="-9599490" label="KernelnextVMs:disabled"/>
   <int value="-5052940" label="enable-simplified-fullscreen"/>
   <int value="-3093629" label="DcheckIsFatal:disabled"/>
   <int value="-2953333" label="AndroidHistoryManager:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 5e716c00..2162b97 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4025,6 +4025,17 @@
   </summary>
 </histogram>
 
+<histogram name="Apps.AppList.ZeroStateSearchResultRemovalDecision"
+    enum="AppListZeroStateResultRemovalConfirmation" expires_after="2019-12-31">
+  <owner>jennyz@chromium.org</owner>
+  <owner>newcomer@chromium.org</owner>
+  <summary>
+    The decision of the user whether to remove a zero state search result. This
+    is gathered per click of a remove or cancel button of a search result
+    removal confirmation dialog.
+  </summary>
+</histogram>
+
 <histogram name="Apps.AppList.ZeroStateSearchResultUserActionType"
     enum="AppListZeroStateSearchResultUserActionType"
     expires_after="2019-12-31">
@@ -4038,6 +4049,10 @@
 
 <histogram name="Apps.AppList.ZeroStateSearchResutRemovalDecision"
     enum="AppListZeroStateResultRemovalConfirmation" expires_after="2019-12-31">
+  <obsolete>
+    Deprecated June 2019, replaced by
+    Apps.AppList.ZeroStateSearchResultRemovalDecision.
+  </obsolete>
   <owner>jennyz@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -12578,7 +12593,7 @@
 </histogram>
 
 <histogram name="Blink.Fonts.HarfBuzzFaceZeroCopyAccess" enum="BooleanSuccess"
-    expires_after="M77">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Counts success or failure of attempting to access font tables using the zero
@@ -12597,7 +12612,7 @@
 </histogram>
 
 <histogram name="Blink.Fonts.VariableFontsRatio"
-    enum="WebFontInstantiationResult" expires_after="M77">
+    enum="WebFontInstantiationResult" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Tracks adoption ratio of variable fonts compared to conventional (in the
@@ -12607,7 +12622,7 @@
 </histogram>
 
 <histogram name="Blink.Fonts.WindowsUniqueLocalFontInstantiationResult"
-    enum="WindowsUniqueLocalFontInstantiationResult" expires_after="M78">
+    enum="WindowsUniqueLocalFontInstantiationResult" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Tracks whether a locally uniquely matched font can be instantiated inside
@@ -26123,7 +26138,7 @@
   </summary>
 </histogram>
 
-<histogram name="DirectWrite.Fonts.Gfx.InitializeLoopCount" expires_after="M77">
+<histogram name="DirectWrite.Fonts.Gfx.InitializeLoopCount" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
@@ -26175,7 +26190,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.CreateFontFaceResult" enum="Hresult"
-    expires_after="M77">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>Records the error returned from CreateFontFace.</summary>
 </histogram>
@@ -26186,7 +26201,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.FallbackResult"
-    enum="DirectWriteFontFallbackResult" expires_after="M77">
+    enum="DirectWriteFontFallbackResult" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Measures the effectiveness of the font fallback proxy. This tracks if we
@@ -26196,7 +26211,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.FamilyCount" units="families"
-    expires_after="M77">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     The number of font families as seen by the font proxy in the renderer.
@@ -26212,13 +26227,13 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.GetSystemFontCollectionResult"
-    enum="Hresult" expires_after="M77">
+    enum="Hresult" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>Records the error returned from GetSystemFontCollection.</summary>
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.IndexingSpeed"
-    units="font files per second" expires_after="M78">
+    units="font files per second" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Indexing speed in font files per second while building the local font unique
@@ -26228,7 +26243,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LastResortFontCount" units="fonts"
-    expires_after="M77">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     The number of last resort fallback fonts found on the system as seen by the
@@ -26237,7 +26252,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LastResortFontFileCount" units="files"
-    expires_after="M77">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     The number of font files found for a last resort fallback font.
@@ -26245,7 +26260,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LoaderType"
-    enum="DirectWriteFontLoaderType" expires_after="M77">
+    enum="DirectWriteFontLoaderType" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     The codepath that was used to load a font family. This is logged in the
@@ -26255,7 +26270,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LoadFamilyResult"
-    enum="DirectWriteLoadFamilyResult" expires_after="M77">
+    enum="DirectWriteLoadFamilyResult" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     The outcome of attempting to load a font family in the renderer (success vs
@@ -26265,7 +26280,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LoadFamilyTime" units="ms"
-    expires_after="M77">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     The time taken to load a font family, excluding glyph data. This is logged
@@ -26274,7 +26289,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LookupTableBuildTime" units="ms"
-    expires_after="M78">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Time it takes to build the font unique name lookup table. Recorded at the
@@ -26284,7 +26299,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LookupTableDiskCacheHit"
-    units="BooleanSuccess" expires_after="M78">
+    units="BooleanSuccess" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Measures whether the font table lookup structure was loaded from disk cache
@@ -26295,7 +26310,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LookupTablePersistSuccess"
-    units="BooleanSuccess" expires_after="M78">
+    units="BooleanSuccess" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Measures whether the font table lookup structure was successfully persisted
@@ -26305,7 +26320,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LookupTableReadyTime" units="ms"
-    expires_after="M78">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Time it takes from scheduling the preparation of the font lookup table until
@@ -26317,7 +26332,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.LookupTableSize" units="KB"
-    expires_after="M78">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Size of the font unique name lookup table in kilobytes. Recorded at the
@@ -26335,7 +26350,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.NumFontFiles" units="font files"
-    expires_after="M78">
+    expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Number of font files which were encountered during scanning for locally
@@ -26346,7 +26361,7 @@
 </histogram>
 
 <histogram name="DirectWrite.Fonts.Proxy.TableBuildTimedOut"
-    units="BooleanTimedout" expires_after="M78">
+    units="BooleanTimedout" expires_after="M82">
   <owner>drott@chromium.org</owner>
   <summary>
     Shows whether the font lookup table construction timed out. Recorded when
@@ -31814,6 +31829,17 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.Policies.IgnoredByPolicyGroup"
+    enum="EnterprisePolicies" expires_after="M78">
+  <owner>ydago@chromium.org</owner>
+  <summary>
+    A set of enterprise policy rules that are ignored because they do not share
+    the highest priority from their policy atomic group. This is recorded at
+    startup, then every 24 hours. If chrome is not running at the 24 hours mark,
+    this will be recorded at the next startup.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.Policy" enum="EnterprisePolicyType"
     expires_after="2019-03-15">
   <obsolete>
@@ -44984,6 +45010,9 @@
 </histogram>
 
 <histogram name="GPU.OopRaster.PaintOpSerializationSize" units="bytes">
+  <obsolete>
+    Deprecated 06/2019.
+  </obsolete>
   <owner>khushalsagar@chromium.org</owner>
   <summary>
     This records the number of bytes required to serialize a paint op for OOP
@@ -55228,7 +55257,8 @@
   </summary>
 </histogram>
 
-<histogram name="Media.IsStreaming" enum="Boolean" expires_after="M77">
+<histogram name="Media.IsStreaming" enum="Boolean" expires_after="2020-06-03">
+  <owner>dalecurtis@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <summary>
     Whether the WMPI data source is streaming (does not support range requests).
@@ -92553,6 +92583,23 @@
 <histogram
     name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess"
     enum="FrameSiteInstanceProcessRelationship" expires_after="2019-09-30">
+  <obsolete>
+    Deprecated 2019-06-04 in favour of variant 2.
+  </obsolete>
+  <owner>chrisha@chromium.org</owner>
+  <owner>catan-team@chromium.org</owner>
+  <summary>
+    Tracks the types of frames that are being hosted by a process over its
+    entire lifetime. An entry in the
+    &quot;AllFramesHaveDistinctSiteInstances&quot; means that the process only
+    ever hosted frames with distinct site instances over its entire lifetime.
+    Recorded on state changes and every 5 minutes.
+  </summary>
+</histogram>
+
+<histogram
+    name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess2"
+    enum="FrameSiteInstanceProcessRelationship" expires_after="2019-09-30">
   <owner>chrisha@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -92567,6 +92614,21 @@
 <histogram
     name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByTime"
     enum="FrameSiteInstanceProcessRelationship" expires_after="2019-09-30">
+  <obsolete>
+    Deprecated 2019-06-04 in favour of variant 2.
+  </obsolete>
+  <owner>chrisha@chromium.org</owner>
+  <owner>catan-team@chromium.org</owner>
+  <summary>
+    Tracks the types of frames that are being hosted by a process, aggregated
+    over time. Each entry in each bucket corresponds to a process being in that
+    state for one second. Recorded on state changes and every 5 minutes.
+  </summary>
+</histogram>
+
+<histogram
+    name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByTime2"
+    enum="FrameSiteInstanceProcessRelationship" expires_after="2019-09-30">
   <owner>chrisha@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -100149,7 +100211,7 @@
 </histogram>
 
 <histogram name="Previews.ServerLitePage.URLLoader.Attempted"
-    enum="BooleanAttempted" expires_after="2019-08-30">
+    enum="BooleanAttempted" expires_after="2020-08-30">
   <owner>ryansturm@chromium.org</owner>
   <summary>
     Whether or not the server lite page preview was attempted during a
@@ -147632,6 +147694,16 @@
       name="Autofill.StrikeDatabase.StrikesPresentWhenStrikeExpired"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="AutofillSuggestionAcceptedIndexSuggestionType"
+    separator=".">
+  <suffix name="CreditCard" label="Suggestions with credit card data"/>
+  <suffix name="Other" label="Unspecified suggestions"/>
+  <suffix name="Profile"
+      label="Suggestions with personal data from AutofillProfiles, e.g. name,
+             address, email address, and or phone number"/>
+  <affected-histogram name="Autofill.SuggestionAcceptedIndex"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="AutofillSyncState" separator=".">
   <suffix name="SignedIn" label="Signed in"/>
   <suffix name="SignedInAndSyncFeature" label="Signed in and sync feature">
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 6cf6c47..9d45779 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -1091,7 +1091,11 @@
     # to ~2 hrs.
     'hard_timeout': 10 * 60 * 60, # 10 hours timeout for full suite
     'ignore_task_failure': False,
-    'io_timeout': 30 * 60, # 30 minutes
+    # 4 hour timeout. Note that this is effectively the timeout for a
+    # benchmarking subprocess to run since we intentionally do not stream
+    # subprocess output to the task stdout.
+    # TODO(crbug.com/865538): Reduce this once we can reduce hard_timeout.
+    'io_timeout': 4 * 60 * 60,
     'dimension_sets': [
       tester_config['dimension']
     ],
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 38f9a0c9..4bc1b21 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -53,206 +53,6 @@
       os.remove(fake_perf_waterfall_file)
 
 
-  def testGenerateCPlusPlusTestSuite(self):
-    swarming_dimensions = [
-        {'os': 'SkyNet', 'pool': 'T-RIP'}
-    ]
-    test_config = {
-        'platform': 'win',
-        'dimension': swarming_dimensions,
-    }
-    test = {
-        'isolate': 'angle_perftest',
-        'num_shards': 1,
-        'type': perf_data_generator.TEST_TYPES.GTEST,
-    }
-    returned_test = perf_data_generator.generate_performance_test(
-        test_config, test)
-
-    expected_generated_test = {
-        'override_compile_targets': ['angle_perftest'],
-        'isolate_name': 'angle_perftest',
-        'args': ['--gtest-benchmark-name', 'angle_perftest'],
-        'trigger_script': {
-          'args': [
-            '--multiple-dimension-script-verbose',
-            'True'
-          ],
-          'requires_simultaneous_shard_dispatch': True,
-          'script': '//testing/trigger_scripts/perf_device_trigger.py'
-        },
-        'merge': {
-          'script': '//tools/perf/process_perf_results.py'
-        },
-        'swarming': {
-          'ignore_task_failure': False,
-          'can_use_on_swarming_builders': True,
-          'expiration': 7200,
-          'io_timeout': 1800,
-          'hard_timeout': 36000,
-          'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
-          'shards': 1
-        },
-        'name': 'angle_perftest'
-      }
-    self.assertEquals(returned_test, expected_generated_test)
-
-  def testGeneratePerformanceTestSuiteExact(self):
-    swarming_dimensions = [
-        {'os': 'SkyNet', 'pool': 'T-RIP'}
-    ]
-    test_config = {
-        'platform': 'android-webview',
-        'browser': 'bin/monochrome_64_32_bundle',
-        'dimension': swarming_dimensions,
-    }
-    test = {
-        'isolate': 'performance_test_suite',
-        'extra_args': [
-            '--run-ref-build',
-            '--test-shard-map-filename=shard_map.json',
-          ],
-        'num_shards': 26
-    }
-    returned_test = perf_data_generator.generate_performance_test(
-        test_config, test)
-
-    expected_generated_test = {
-        'override_compile_targets': ['performance_test_suite'],
-        'isolate_name': 'performance_test_suite',
-        'args': ['-v', '--browser=exact', '--upload-results',
-                 '--browser-executable=../../out/Release'
-                 '/bin/monochrome_64_32_bundle',
-                 '--device=android',
-                 '--webview-embedder-apk=../../out/Release'
-                 '/apks/SystemWebViewShell.apk',
-                 '--run-ref-build',
-                 '--test-shard-map-filename=shard_map.json'],
-        'trigger_script': {
-          'args': [
-            '--multiple-dimension-script-verbose',
-            'True'
-          ],
-          'requires_simultaneous_shard_dispatch': True,
-          'script': '//testing/trigger_scripts/perf_device_trigger.py'
-        },
-        'merge': {
-          'script': '//tools/perf/process_perf_results.py'
-        },
-        'swarming': {
-          'ignore_task_failure': False,
-          'can_use_on_swarming_builders': True,
-          'expiration': 7200,
-          'io_timeout': 1800,
-          'hard_timeout': 36000,
-          'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
-          'shards': 26
-        },
-        'name': 'performance_test_suite'
-      }
-    self.assertEquals(returned_test, expected_generated_test)
-
-  def testGeneratePerformanceTestSuiteWebview(self):
-    swarming_dimensions = [
-        {'os': 'SkyNet', 'pool': 'T-RIP'}
-    ]
-    test_config = {
-        'platform': 'android-webview',
-        'dimension': swarming_dimensions,
-    }
-    test = {
-        'isolate': 'performance_test_suite',
-        'extra_args': [
-            '--run-ref-build',
-            '--test-shard-map-filename=shard_map.json',
-          ],
-        'num_shards': 26
-    }
-    returned_test = perf_data_generator.generate_performance_test(
-        test_config, test)
-
-    expected_generated_test = {
-        'override_compile_targets': ['performance_test_suite'],
-        'isolate_name': 'performance_test_suite',
-        'args': ['-v', '--browser=android-webview', '--upload-results',
-                 '--webview-embedder-apk=../../out/Release'
-                 '/apks/SystemWebViewShell.apk',
-                 '--run-ref-build',
-                 '--test-shard-map-filename=shard_map.json'],
-        'trigger_script': {
-          'args': [
-            '--multiple-dimension-script-verbose',
-            'True'
-          ],
-          'requires_simultaneous_shard_dispatch': True,
-          'script': '//testing/trigger_scripts/perf_device_trigger.py'
-        },
-        'merge': {
-          'script': '//tools/perf/process_perf_results.py'
-        },
-        'swarming': {
-          'ignore_task_failure': False,
-          'can_use_on_swarming_builders': True,
-          'expiration': 7200,
-          'io_timeout': 1800,
-          'hard_timeout': 36000,
-          'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
-          'shards': 26
-        },
-        'name': 'performance_test_suite'
-      }
-    self.assertEquals(returned_test, expected_generated_test)
-
-  def testGeneratePerformanceTestSuite(self):
-    swarming_dimensions = [
-        {'os': 'SkyNet', 'pool': 'T-RIP'}
-    ]
-    test_config = {
-        'platform': 'android',
-        'dimension': swarming_dimensions,
-    }
-    test = {
-        'isolate': 'performance_test_suite',
-        'extra_args': [
-            '--run-ref-build',
-            '--test-shard-map-filename=shard_map.json',
-          ],
-        'num_shards': 26
-    }
-    returned_test = perf_data_generator.generate_performance_test(
-        test_config, test)
-
-    expected_generated_test = {
-        'override_compile_targets': ['performance_test_suite'],
-        'isolate_name': 'performance_test_suite',
-        'args': ['-v', '--browser=android-chromium', '--upload-results',
-                 '--run-ref-build',
-                 '--test-shard-map-filename=shard_map.json'],
-        'trigger_script': {
-          'args': [
-            '--multiple-dimension-script-verbose',
-            'True'
-          ],
-          'requires_simultaneous_shard_dispatch': True,
-          'script': '//testing/trigger_scripts/perf_device_trigger.py'
-        },
-        'merge': {
-          'script': '//tools/perf/process_perf_results.py'
-        },
-        'swarming': {
-          'ignore_task_failure': False,
-          'can_use_on_swarming_builders': True,
-          'expiration': 7200,
-          'io_timeout': 1800,
-          'hard_timeout': 36000,
-          'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
-          'shards': 26
-        },
-        'name': 'performance_test_suite'
-      }
-    self.assertEquals(returned_test, expected_generated_test)
-
-
 class TestIsPerfBenchmarksSchedulingValid(unittest.TestCase):
   def setUp(self):
     self.maxDiff = None
diff --git a/ui/base/cursor/OWNERS b/ui/base/cursor/OWNERS
deleted file mode 100644
index c3e460e..0000000
--- a/ui/base/cursor/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file *x11*=derat@chromium.org
diff --git a/ui/base/x/OWNERS b/ui/base/x/OWNERS
index 20cb707..9b39f6f 100644
--- a/ui/base/x/OWNERS
+++ b/ui/base/x/OWNERS
@@ -1,3 +1,3 @@
-davemoore@chromium.org
+piman@chromium.org
 sadrul@chromium.org
-derat@chromium.org
+thomasanderson@chromium.org
diff --git a/ui/chromeos/OWNERS b/ui/chromeos/OWNERS
index 94f5eddc..734b95a3 100644
--- a/ui/chromeos/OWNERS
+++ b/ui/chromeos/OWNERS
@@ -1,4 +1,3 @@
-derat@chromium.org
 jamescook@chromium.org
 oshima@chromium.org
 stevenjb@chromium.org
diff --git a/ui/display/OWNERS b/ui/display/OWNERS
index 0101ef32..6f13619 100644
--- a/ui/display/OWNERS
+++ b/ui/display/OWNERS
@@ -1,5 +1,4 @@
 afakhry@chromium.org
-derat@chromium.org
 dnicoara@chromium.org
 marcheu@chromium.org
 oshima@chromium.org
diff --git a/ui/display/mac/display_link_mac.cc b/ui/display/mac/display_link_mac.cc
index 29d6b303..6e0f7cf 100644
--- a/ui/display/mac/display_link_mac.cc
+++ b/ui/display/mac/display_link_mac.cc
@@ -70,9 +70,6 @@
   if (!display_id)
     return nullptr;
 
-  // Ensure the main thread is captured.
-  GetMainThreadTaskRunner();
-
   // Return the existing display link for this display, if it exists.
   DisplayLinkMap& all_display_links = GetAllDisplayLinks();
   auto found = all_display_links.find(display_id);
@@ -102,6 +99,36 @@
   return display_link_mac;
 }
 
+bool DisplayLinkMac::GetVSyncParameters(base::TimeTicks* timebase,
+                                        base::TimeDelta* interval) {
+  if (!timebase_and_interval_valid_) {
+    StartOrContinueDisplayLink();
+    return false;
+  }
+
+  // The vsync parameters skew over time (astonishingly quickly -- 0.1 msec per
+  // second). If too much time has elapsed since the last time the vsync
+  // parameters were calculated, re-calculate them (but still return the old
+  // parameters -- the update will be asynchronous).
+  if (base::TimeTicks::Now() >= recalculate_time_)
+    StartOrContinueDisplayLink();
+
+  *timebase = timebase_;
+  *interval = interval_;
+  return true;
+}
+
+double DisplayLinkMac::GetRefreshRate() {
+  double refresh_rate = 0;
+  CVTime cv_time =
+      CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link_);
+  if (!(cv_time.flags & kCVTimeIsIndefinite))
+    refresh_rate = (static_cast<double>(cv_time.timeScale) /
+                    static_cast<double>(cv_time.timeValue));
+
+  return refresh_rate;
+}
+
 DisplayLinkMac::DisplayLinkMac(
     CGDirectDisplayID display_id,
     base::ScopedTypeRef<CVDisplayLinkRef> display_link)
@@ -133,25 +160,6 @@
   }
 }
 
-bool DisplayLinkMac::GetVSyncParameters(base::TimeTicks* timebase,
-                                        base::TimeDelta* interval) {
-  if (!timebase_and_interval_valid_) {
-    StartOrContinueDisplayLink();
-    return false;
-  }
-
-  // The vsync parameters skew over time (astonishingly quickly -- 0.1 msec per
-  // second). If too much time has elapsed since the last time the vsync
-  // parameters were calculated, re-calculate them (but still return the old
-  // parameters -- the update will be asynchronous).
-  if (base::TimeTicks::Now() >= recalculate_time_)
-    StartOrContinueDisplayLink();
-
-  *timebase = timebase_;
-  *interval = interval_;
-  return true;
-}
-
 // static
 void DisplayLinkMac::DoUpdateVSyncParameters(CGDirectDisplayID display,
                                              const CVTimeStamp& time) {
@@ -207,6 +215,10 @@
   if (CVDisplayLinkIsRunning(display_link_))
     return;
 
+  // Ensure the main thread is captured.
+  if (!task_runner_)
+    task_runner_ = GetMainThreadTaskRunner();
+
   CVReturn ret = CVDisplayLinkStart(display_link_);
   if (ret != kCVReturnSuccess)
     LOG(ERROR) << "CVDisplayLinkStart failed: " << ret;
diff --git a/ui/display/mac/display_link_mac.h b/ui/display/mac/display_link_mac.h
index c7e23ba..3db8d3cb 100644
--- a/ui/display/mac/display_link_mac.h
+++ b/ui/display/mac/display_link_mac.h
@@ -11,6 +11,7 @@
 
 #include "base/mac/scoped_typeref.h"
 #include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "ui/display/display_export.h"
 
@@ -27,6 +28,9 @@
   // are invalid.
   bool GetVSyncParameters(base::TimeTicks* timebase, base::TimeDelta* interval);
 
+  // Get the panel/monitor refresh rate
+  double GetRefreshRate();
+
  private:
   friend class base::RefCountedThreadSafe<DisplayLinkMac>;
 
@@ -66,6 +70,9 @@
   // CVDisplayLink for querying VSync timing info.
   base::ScopedTypeRef<CVDisplayLinkRef> display_link_;
 
+  // The task runner to post tasks to from the display link thread.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
   // VSync parameters computed during UpdateVSyncParameters().
   bool timebase_and_interval_valid_ = false;
   base::TimeTicks timebase_;
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
index 4d5b83a..170963e 100644
--- a/ui/display/mac/screen_mac.mm
+++ b/ui/display/mac/screen_mac.mm
@@ -21,6 +21,7 @@
 #include "base/timer/timer.h"
 #include "ui/display/display.h"
 #include "ui/display/display_change_notifier.h"
+#include "ui/display/mac/display_link_mac.h"
 #include "ui/gfx/icc_profile.h"
 #include "ui/gfx/mac/coordinate_conversion.h"
 
@@ -109,6 +110,9 @@
   display.set_depth_per_component(NSBitsPerSampleFromDepth([screen depth]));
   display.set_is_monochrome(CGDisplayUsesForceToGray());
 
+  if (auto display_link = ui::DisplayLinkMac::GetForDisplay(display_id))
+    display.set_display_frequency(display_link->GetRefreshRate());
+
   // CGDisplayRotation returns a double. Display::SetRotationAsDegree will
   // handle the unexpected situations were the angle is not a multiple of 90.
   display.SetRotationAsDegree(static_cast<int>(CGDisplayRotation(display_id)));
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
index 6cf94ae..f752dd29 100644
--- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
@@ -97,15 +97,17 @@
 
   isScanningForTest = false;
 
-  directoryModel = /** @type {!DirectoryModel} */ ({
-    __proto__: cr.EventTarget.prototype,
-    getFileList: function() {
+  class TestDirectoryModel extends cr.EventTarget {
+    getFileList() {
       return fileListModel;
-    },
-    isScanning: function() {
+    }
+    isScanning() {
       return isScanningForTest;
-    },
-  });
+    }
+  }
+
+  /** @suppress {checkTypes} */
+  directoryModel = /** @type {!DirectoryModel} */ (new TestDirectoryModel());
 
   const fakeVolumeManager = /** @type {!VolumeManager} */ ({
     getVolumeInfo: function(entry) {
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS
index 077be260c..f764a45 100644
--- a/ui/gfx/OWNERS
+++ b/ui/gfx/OWNERS
@@ -12,9 +12,6 @@
 per-file display*=oshima@chromium.org
 per-file screen*=oshima@chromium.org
 
-# Font-rendering configuration.
-per-file font_render_params*=derat@chromium.org
-
 # Canvas painting.
 per-file canvas*=danakj@chromium.org
 
diff --git a/ui/gfx/test/OWNERS b/ui/gfx/test/OWNERS
deleted file mode 100644
index fe470e6..0000000
--- a/ui/gfx/test/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Testing support for Linux implementation of FontRenderParams.
-per-file fontconfig_util_linux.*=derat@chromium.org
diff --git a/ui/gfx/x/OWNERS b/ui/gfx/x/OWNERS
index 1b19a9d3..079174d 100644
--- a/ui/gfx/x/OWNERS
+++ b/ui/gfx/x/OWNERS
@@ -1,4 +1,4 @@
-derat@chromium.org
+thomasanderson@chromium.org
 
 # Adding new atoms is allowed without OWNERS review.
 per-file x11_atom_cache.cc=*
diff --git a/ui/gl/features.gni b/ui/gl/features.gni
index a90d220..456990c 100644
--- a/ui/gl/features.gni
+++ b/ui/gl/features.gni
@@ -14,5 +14,5 @@
   # Should Dawn support be compiled to back the WebGPU implementation.
   # Also controls linking Dawn depedencies in such as SPIRV-Tools and
   # SPIRV-Cross
-  use_dawn = is_mac
+  use_dawn = false
 }
diff --git a/ui/native_theme/caption_style_mac.mm b/ui/native_theme/caption_style_mac.mm
index ddc1164..75af604 100644
--- a/ui/native_theme/caption_style_mac.mm
+++ b/ui/native_theme/caption_style_mac.mm
@@ -47,12 +47,14 @@
   return color_utils::SkColorToRgbaString(rgba_color);
 }
 
-std::string GetMABackgroundColorAsCSSColor() {
+std::string GetMABackgroundColorAndOpacityAsCSSColor() {
   base::ScopedCFTypeRef<CGColorRef> cg_color(
       MACaptionAppearanceCopyBackgroundColor(kUserDomain, nullptr));
+  float opacity = MACaptionAppearanceGetBackgroundOpacity(kUserDomain, nullptr);
 
-  return color_utils::SkColorToRgbaString(
-      skia::CGColorRefToSkColor(cg_color.get()));
+  SkColor rgba_color =
+      SkColorSetA(skia::CGColorRefToSkColor(cg_color.get()), 0xff * opacity);
+  return color_utils::SkColorToRgbaString(rgba_color);
 }
 
 // The MA text scale is a float between 0.0 and 2.0; this function converts it
@@ -125,7 +127,7 @@
   CaptionStyle style;
 
   style.text_color = GetMAForegroundColorAndOpacityAsCSSColor();
-  style.background_color = GetMABackgroundColorAsCSSColor();
+  style.background_color = GetMABackgroundColorAndOpacityAsCSSColor();
   style.text_size = GetMATextScaleAsCSSPercent();
   style.text_shadow = GetMATextEdgeStyleAsCSSShadow();
 
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index b71efc47..44e4aca4 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -22,14 +22,6 @@
   memcpy(this, &other, sizeof(*this));
 }
 
-void NativeTheme::SetScrollbarColors(unsigned inactive_color,
-                                     unsigned active_color,
-                                     unsigned track_color) {
-  thumb_inactive_color_ = inactive_color;
-  thumb_active_color_ = active_color;
-  track_color_ = track_color;
-}
-
 void NativeTheme::AddObserver(NativeThemeObserver* observer) {
   native_theme_observers_.AddObserver(observer);
 }
@@ -44,10 +36,7 @@
 }
 
 NativeTheme::NativeTheme()
-    : thumb_inactive_color_(0xeaeaea),
-      thumb_active_color_(0xf4f4f4),
-      track_color_(0xd3d3d3),
-      is_dark_mode_(IsForcedDarkMode()),
+    : is_dark_mode_(IsForcedDarkMode()),
       is_high_contrast_(IsForcedHighContrast()) {}
 
 NativeTheme::~NativeTheme() {
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 15f5391..b8a8872 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -284,11 +284,6 @@
   // when the part is resized.
   virtual gfx::Rect GetNinePatchAperture(Part part) const = 0;
 
-  // Supports theme specific colors.
-  void SetScrollbarColors(unsigned inactive_color,
-                          unsigned active_color,
-                          unsigned track_color);
-
   // Colors for GetSystemColor().
   enum ColorId {
     // Windows
@@ -441,10 +436,6 @@
     is_high_contrast_ = is_high_contrast;
   }
 
-  unsigned int thumb_inactive_color_;
-  unsigned int thumb_active_color_;
-  unsigned int track_color_;
-
  private:
   // DarkModeObserver callback.
   void OnParentDarkModeChanged(bool is_dark_mode);
diff --git a/ui/native_theme/native_theme_base.cc b/ui/native_theme/native_theme_base.cc
index c2d9778b..b07dd41 100644
--- a/ui/native_theme/native_theme_base.cc
+++ b/ui/native_theme/native_theme_base.cc
@@ -36,6 +36,10 @@
 const int kSliderThumbWidth = 11;
 const int kSliderThumbHeight = 21;
 
+constexpr SkColor kThumbActiveColor = SkColorSetRGB(0xF4, 0xF4, 0xF4);
+constexpr SkColor kThumbInactiveColor = SkColorSetRGB(0xEA, 0xEA, 0xEA);
+constexpr SkColor kTrackColor = SkColorSetRGB(0xD3, 0xD3, 0xD3);
+
 const SkColor kSliderTrackBackgroundColor =
     SkColorSetRGB(0xe3, 0xdd, 0xd8);
 const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef);
@@ -280,7 +284,7 @@
 
   // Calculate button color.
   SkScalar trackHSV[3];
-  SkColorToHSV(track_color_, trackHSV);
+  SkColorToHSV(kTrackColor, trackHSV);
   SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2f);
   SkColor backgroundColor = buttonColor;
   if (state == kPressed) {
@@ -347,7 +351,7 @@
   flags.setAntiAlias(true);
   flags.setStyle(cc::PaintFlags::kStroke_Style);
   SkScalar thumbHSV[3];
-  SkColorToHSV(thumb_inactive_color_, thumbHSV);
+  SkColorToHSV(kThumbInactiveColor, thumbHSV);
   flags.setColor(OutlineColor(trackHSV, thumbHSV));
   canvas->drawPath(outline, flags);
 
@@ -422,12 +426,12 @@
 
   skrect.set(rect.x(), rect.y(), rect.right(), rect.bottom());
   SkScalar track_hsv[3];
-  SkColorToHSV(track_color_, track_hsv);
+  SkColorToHSV(kTrackColor, track_hsv);
   flags.setColor(SaturateAndBrighten(track_hsv, 0, 0));
   canvas->drawIRect(skrect, flags);
 
   SkScalar thumb_hsv[3];
-  SkColorToHSV(thumb_inactive_color_, thumb_hsv);
+  SkColorToHSV(kThumbInactiveColor, thumb_hsv);
 
   flags.setColor(OutlineColor(track_hsv, thumb_hsv));
   DrawBox(canvas, rect, flags);
@@ -444,7 +448,7 @@
   const bool vertical = part == kScrollbarVerticalThumb;
 
   SkScalar thumb[3];
-  SkColorToHSV(hovered ? thumb_active_color_ : thumb_inactive_color_, thumb);
+  SkColorToHSV(hovered ? kThumbActiveColor : kThumbInactiveColor, thumb);
 
   cc::PaintFlags flags;
   flags.setColor(SaturateAndBrighten(thumb, 0, 0.02f));
@@ -470,7 +474,7 @@
   canvas->drawIRect(skrect, flags);
 
   SkScalar track[3];
-  SkColorToHSV(track_color_, track);
+  SkColorToHSV(kTrackColor, track);
   flags.setColor(OutlineColor(track, thumb));
   DrawBox(canvas, rect, flags);
 
@@ -941,9 +945,9 @@
     return SK_ColorBLACK;
 
   SkScalar track_hsv[3];
-  SkColorToHSV(track_color_, track_hsv);
+  SkColorToHSV(kTrackColor, track_hsv);
   SkScalar thumb_hsv[3];
-  SkColorToHSV(thumb_inactive_color_, thumb_hsv);
+  SkColorToHSV(kThumbInactiveColor, thumb_hsv);
   return OutlineColor(track_hsv, thumb_hsv);
 }
 
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm b/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm
index bf20c20..5a1d1f2 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm
@@ -31,12 +31,12 @@
   if (!top_level_widget)
     return nil;
 
-  auto* bridge_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
+  auto* window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
       top_level_widget->GetNativeWindow());
-  if (!bridge_host)
+  if (!window_host)
     return nil;
 
-  return bridge_host->GetNativeViewAccessibleForNSWindow();
+  return window_host->GetNativeViewAccessibleForNSWindow();
 }
 
 gfx::NativeViewAccessible ViewAXPlatformNodeDelegateMac::GetParent() {
@@ -47,12 +47,12 @@
   if (!widget)
     return nil;
 
-  auto* bridge_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
+  auto* window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
       view()->GetWidget()->GetNativeWindow());
-  if (!bridge_host)
+  if (!window_host)
     return nil;
 
-  return bridge_host->GetNativeViewAccessibleForNSView();
+  return window_host->GetNativeViewAccessibleForNSView();
 }
 
 }  // namespace views
diff --git a/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm b/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
index c56c2f6..736f611 100644
--- a/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
+++ b/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
@@ -257,7 +257,7 @@
   HitTestNativeWidgetMac(internal::NativeWidgetDelegate* delegate,
                          NativeFrameView* native_frame_view)
       : NativeWidgetMac(delegate), native_frame_view_(native_frame_view) {
-    bridge_host_ = std::make_unique<NativeWidgetMacNSWindowHost>(this);
+    ns_window_host_ = std::make_unique<NativeWidgetMacNSWindowHost>(this);
   }
 
   // internal::NativeWidgetPrivate:
diff --git a/ui/views/cocoa/bridged_native_widget_unittest.mm b/ui/views/cocoa/bridged_native_widget_unittest.mm
index 5241c96..d925401 100644
--- a/ui/views/cocoa/bridged_native_widget_unittest.mm
+++ b/ui/views/cocoa/bridged_native_widget_unittest.mm
@@ -300,8 +300,8 @@
  public:
   explicit MockNativeWidgetMac(internal::NativeWidgetDelegate* delegate)
       : NativeWidgetMac(delegate) {}
-  using NativeWidgetMac::bridge_impl;
-  using NativeWidgetMac::bridge_host;
+  using NativeWidgetMac::GetInProcessNSWindowBridge;
+  using NativeWidgetMac::GetNSWindowHost;
 
   // internal::NativeWidgetPrivate:
   void InitNativeWidget(const Widget::InitParams& params) override {
@@ -313,19 +313,19 @@
                       styleMask:NSBorderlessWindowMask
                         backing:NSBackingStoreBuffered
                           defer:NO]);
-    bridge_host()->CreateLocalBridge(window);
+    GetNSWindowHost()->CreateInProcessNSWindowBridge(window);
     if (auto* parent =
             NativeWidgetMacNSWindowHost::GetFromNativeView(params.parent)) {
-      bridge_host()->SetParent(parent);
+      GetNSWindowHost()->SetParent(parent);
     }
-    bridge_host()->InitWindow(params);
+    GetNSWindowHost()->InitWindow(params);
 
     // Usually the bridge gets initialized here. It is skipped to run extra
     // checks in tests, and so that a second window isn't created.
     delegate()->OnNativeWidgetCreated();
 
     // To allow events to dispatch to a view, it needs a way to get focus.
-    bridge_host()->SetFocusManager(GetWidget()->GetFocusManager());
+    GetNSWindowHost()->SetFocusManager(GetWidget()->GetFocusManager());
   }
 
   void ReorderNativeViews() override {
@@ -350,10 +350,10 @@
       : native_widget_mac_(nullptr) {}
 
   remote_cocoa::NativeWidgetNSWindowBridge* bridge() {
-    return native_widget_mac_->bridge_impl();
+    return native_widget_mac_->GetInProcessNSWindowBridge();
   }
-  NativeWidgetMacNSWindowHost* bridge_host() {
-    return native_widget_mac_->bridge_host();
+  NativeWidgetMacNSWindowHost* GetNSWindowHost() {
+    return native_widget_mac_->GetNSWindowHost();
   }
 
   // Generate an autoreleased KeyDown NSEvent* in |widget_| for pressing the
@@ -405,8 +405,8 @@
   }
 
   NSWindow* bridge_window() const {
-    if (native_widget_mac_->bridge_impl())
-      return native_widget_mac_->bridge_impl()->ns_window();
+    if (auto* bridge = native_widget_mac_->GetInProcessNSWindowBridge())
+      return bridge->ns_window();
     return nil;
   }
 
@@ -551,7 +551,7 @@
   // schedules a task to flash the cursor, so this requires |message_loop_|.
   textfield->RequestFocus();
 
-  bridge_host()->text_input_host()->SetTextInputClient(textfield);
+  GetNSWindowHost()->text_input_host()->SetTextInputClient(textfield);
 
   // Initialize the dummy text view. Initializing this with NSZeroRect causes
   // weird NSTextView behavior on OSX 10.9.
@@ -633,8 +633,8 @@
 
   // The delegate should exist before setting the root view.
   EXPECT_TRUE([window delegate]);
-  bridge_host()->SetRootView(view_.get());
-  bridge()->CreateContentView(bridge_host()->GetRootViewNSViewId(),
+  GetNSWindowHost()->SetRootView(view_.get());
+  bridge()->CreateContentView(GetNSWindowHost()->GetRootViewNSViewId(),
                               view_->bounds());
   ns_view_ = bridge()->ns_view();
 
@@ -647,8 +647,8 @@
   // Clear kill buffer so that no state persists between tests.
   TextfieldModel::ClearKillBuffer();
 
-  if (bridge_host()) {
-    bridge_host()->SetRootView(nullptr);
+  if (GetNSWindowHost()) {
+    GetNSWindowHost()->SetRootView(nullptr);
     bridge()->DestroyContentView();
   }
   view_.reset();
@@ -850,7 +850,7 @@
 }
 
 TEST_F(BridgedNativeWidgetTest, GetInputMethodShouldNotReturnNull) {
-  EXPECT_TRUE(bridge_host()->GetInputMethod());
+  EXPECT_TRUE(GetNSWindowHost()->GetInputMethod());
 }
 
 // A simpler test harness for testing initialization flows.
@@ -884,7 +884,7 @@
       new MockNativeWidgetMac(nullptr));
   native_widget_mac_ = native_widget.get();
   EXPECT_FALSE(bridge());
-  EXPECT_FALSE(bridge_host()->GetLocalNSWindow());
+  EXPECT_FALSE(GetNSWindowHost()->GetInProcessNSWindow());
 }
 
 // Tests the shadow type given in InitParams.
@@ -931,7 +931,7 @@
   EXPECT_FALSE([ns_view_ inputContext]);
   InstallTextField(test_string, ui::TEXT_INPUT_TYPE_TEXT);
   EXPECT_TRUE([ns_view_ inputContext]);
-  bridge_host()->text_input_host()->SetTextInputClient(nullptr);
+  GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr);
   EXPECT_FALSE([ns_view_ inputContext]);
   InstallTextField(test_string, ui::TEXT_INPUT_TYPE_NONE);
   EXPECT_FALSE([ns_view_ inputContext]);
@@ -1340,7 +1340,7 @@
 // Test that we don't crash during an action message even if the TextInputClient
 // is nil. Regression test for crbug.com/615745.
 TEST_F(BridgedNativeWidgetTest, NilTextInputClient) {
-  bridge_host()->text_input_host()->SetTextInputClient(nullptr);
+  GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr);
   NSMutableArray* selectors = [NSMutableArray array];
   [selectors addObjectsFromArray:kMoveActions];
   [selectors addObjectsFromArray:kSelectActions];
@@ -1778,7 +1778,7 @@
         // Now, the |textfield| set above should have been set again.
         EXPECT_TRUE(g_fake_current_input_context);
       },
-      &saw_update_windows, ns_view_, bridge_host(), textfield);
+      &saw_update_windows, ns_view_, GetNSWindowHost(), textfield);
 
   SetHandleKeyEventCallback(base::BindRepeating(
       [](int* saw_return_count, BridgedContentView* view,
@@ -1792,7 +1792,7 @@
         }
         return false;
       },
-      &vkey_return_count, ns_view_, bridge_host()));
+      &vkey_return_count, ns_view_, GetNSWindowHost()));
 
   // Starting text (just insert it).
   [ns_view_ insertText:@"ã…‚" replacementRange:NSMakeRange(NSNotFound, 0)];
diff --git a/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
index eae4978..c4084bc 100644
--- a/ui/views/cocoa/drag_drop_client_mac_unittest.mm
+++ b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
@@ -158,7 +158,7 @@
   DragDropClientMacTest() : widget_(new Widget) {}
 
   DragDropClientMac* drag_drop_client() {
-    return bridge_host_->drag_drop_client();
+    return ns_window_host_->drag_drop_client();
   }
 
   NSDragOperation DragUpdate(NSPasteboard* pasteboard) {
@@ -189,9 +189,9 @@
     gfx::Rect bounds(0, 0, 100, 100);
     widget_->SetBounds(bounds);
 
-    bridge_host_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
+    ns_window_host_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
         widget_->GetNativeWindow());
-    bridge_ = bridge_host_->bridge_impl();
+    bridge_ = ns_window_host_->GetInProcessNSWindowBridge();
     widget_->Show();
 
     target_ = new DragDropView();
@@ -210,7 +210,7 @@
  protected:
   Widget* widget_ = nullptr;
   remote_cocoa::NativeWidgetNSWindowBridge* bridge_ = nullptr;
-  NativeWidgetMacNSWindowHost* bridge_host_ = nullptr;
+  NativeWidgetMacNSWindowHost* ns_window_host_ = nullptr;
   DragDropView* target_ = nullptr;
   base::scoped_nsobject<MockDraggingInfo> dragging_info_;
 
@@ -242,7 +242,7 @@
   // since the runloop will exit before the system has any opportunity to
   // capture anything.
   bridge_->AcquireCapture();
-  EXPECT_TRUE(bridge_host_->IsMouseCaptureActive());
+  EXPECT_TRUE(ns_window_host_->IsMouseCaptureActive());
 
   // Create the drop data
   OSExchangeData data;
@@ -268,7 +268,7 @@
       target_, data, 0, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
 
   // The capture should be released.
-  EXPECT_FALSE(bridge_host_->IsMouseCaptureActive());
+  EXPECT_FALSE(ns_window_host_->IsMouseCaptureActive());
 }
 
 // Tests if the drag and drop target rejects the dropped data with the
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.h b/ui/views/cocoa/native_widget_mac_ns_window_host.h
index aab57676..cc22aeee 100644
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.h
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.h
@@ -96,7 +96,7 @@
   // A NSWindow that is guaranteed to exist in this process. If the bridge
   // object for this host is in this process, then this points to the bridge's
   // NSWindow. Otherwise, it mirrors the id and bounds of the child window.
-  NativeWidgetMacNSWindow* GetLocalNSWindow() const;
+  NativeWidgetMacNSWindow* GetInProcessNSWindow() const;
 
   // Return the accessibility object for the content NSView.
   gfx::NativeViewAccessible GetNativeViewAccessibleForNSView() const;
@@ -105,14 +105,15 @@
   gfx::NativeViewAccessible GetNativeViewAccessibleForNSWindow() const;
 
   // The mojo interface through which to communicate with the underlying
-  // NSWindow and NSView.
-  remote_cocoa::mojom::NativeWidgetNSWindow* bridge() const;
+  // NSWindow and NSView. This points to either |remote_ns_window_ptr_| or
+  // |in_process_ns_window_bridge_|.
+  remote_cocoa::mojom::NativeWidgetNSWindow* GetNSWindowMojo() const;
 
   // Direct access to the NativeWidgetNSWindowBridge that this is hosting.
   // TODO(ccameron): Remove all accesses to this member, and replace them
   // with methods that may be sent across processes.
-  remote_cocoa::NativeWidgetNSWindowBridge* bridge_impl() const {
-    return bridge_impl_.get();
+  remote_cocoa::NativeWidgetNSWindowBridge* GetInProcessNSWindowBridge() const {
+    return in_process_ns_window_bridge_.get();
   }
 
   TooltipManager* tooltip_manager() { return tooltip_manager_.get(); }
@@ -122,10 +123,11 @@
   }
 
   // Create and set the bridge object to be in this process.
-  void CreateLocalBridge(base::scoped_nsobject<NativeWidgetMacNSWindow> window);
+  void CreateInProcessNSWindowBridge(
+      base::scoped_nsobject<NativeWidgetMacNSWindow> window);
 
   // Create and set the bridge object to be potentially in another process.
-  void CreateRemoteBridge(
+  void CreateRemoteNSWindow(
       remote_cocoa::ApplicationHost* application_host,
       remote_cocoa::mojom::CreateWindowParamsPtr window_create_params);
 
@@ -225,9 +227,9 @@
   void RankNSViewsRecursive(View* view, std::map<NSView*, int>* rank) const;
 
   // If we are accessing the BridgedNativeWidget through mojo, then
-  // |local_window_| is not the true window that is resized. This function
-  // updates the frame of |local_window_| to keep it in sync for any native
-  // calls that may use it (e.g, for context menu positioning).
+  // |in_process_ns_window_| is not the true window that is resized. This
+  // function updates the frame of |in_process_ns_window_| to keep it in sync
+  // for any native calls that may use it (e.g, for context menu positioning).
   void UpdateLocalWindowFrame(const gfx::Rect& frame);
 
   // NativeWidgetNSWindowHostHelper:
@@ -385,8 +387,8 @@
   NativeWidgetMacNSWindowHost* parent_ = nullptr;
   std::vector<NativeWidgetMacNSWindowHost*> children_;
 
-  // The factory that was used to create |bridge_ptr_|. This must be the same
-  // as |parent_->application_host_|.
+  // The factory that was used to create |remote_ns_window_ptr_|. This must be
+  // the same as |parent_->application_host_|.
   remote_cocoa::ApplicationHost* application_host_ = nullptr;
 
   Widget::InitParams::Type widget_type_ = Widget::InitParams::TYPE_WINDOW;
@@ -401,7 +403,7 @@
 
   // The mojo pointer to a BridgedNativeWidget, which may exist in another
   // process.
-  remote_cocoa::mojom::NativeWidgetNSWindowAssociatedPtr bridge_ptr_;
+  remote_cocoa::mojom::NativeWidgetNSWindowAssociatedPtr remote_ns_window_ptr_;
 
   // Remote accessibility objects corresponding to the NSWindow and its root
   // NSView.
@@ -416,13 +418,16 @@
   // TODO(ccameron): Rather than instantiate a NativeWidgetNSWindowBridge here,
   // we will instantiate a mojo NativeWidgetNSWindowBridge interface to a Cocoa
   // instance that may be in another process.
-  std::unique_ptr<remote_cocoa::NativeWidgetNSWindowBridge> bridge_impl_;
+  std::unique_ptr<remote_cocoa::NativeWidgetNSWindowBridge>
+      in_process_ns_window_bridge_;
 
-  // Window that is guaranteed to exist in this process (see GetLocalNSWindow).
-  base::scoped_nsobject<NativeWidgetMacNSWindow> local_window_;
+  // Window that is guaranteed to exist in this process (see
+  // GetInProcessNSWindow).
+  base::scoped_nsobject<NativeWidgetMacNSWindow> in_process_ns_window_;
 
-  // Id mapping for |local_window_|'s content NSView.
-  std::unique_ptr<remote_cocoa::ScopedNSViewIdMapping> local_view_id_mapping_;
+  // Id mapping for |in_process_ns_window_|'s content NSView.
+  std::unique_ptr<remote_cocoa::ScopedNSViewIdMapping>
+      in_process_view_id_mapping_;
 
   std::unique_ptr<TooltipManager> tooltip_manager_;
   std::unique_ptr<ui::InputMethod> input_method_;
@@ -463,7 +468,7 @@
   std::map<const views::View*, NSView*> associated_views_;
 
   mojo::AssociatedBinding<remote_cocoa::mojom::NativeWidgetNSWindowHost>
-      host_mojo_binding_;
+      remote_ns_window_host_binding_;
   DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacNSWindowHost);
 };
 
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
index 6585b7f..b0afad2 100644
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
@@ -297,7 +297,7 @@
       root_view_id_(remote_cocoa::GetNewNSViewId()),
       accessibility_focus_overrider_(this),
       text_input_host_(new TextInputHost(this)),
-      host_mojo_binding_(this) {
+      remote_ns_window_host_binding_(this) {
   DCHECK(GetIdToWidgetHostImplMap().find(widget_id_) ==
          GetIdToWidgetHostImplMap().end());
   GetIdToWidgetHostImplMap().insert(std::make_pair(widget_id_, this));
@@ -307,14 +307,14 @@
 NativeWidgetMacNSWindowHost::~NativeWidgetMacNSWindowHost() {
   DCHECK(children_.empty());
   if (application_host_) {
-    bridge_ptr_.reset();
+    remote_ns_window_ptr_.reset();
     application_host_->RemoveObserver(this);
     application_host_ = nullptr;
   }
 
   // Workaround for https://crbug.com/915572
-  if (host_mojo_binding_.is_bound()) {
-    auto request = host_mojo_binding_.Unbind();
+  if (remote_ns_window_host_binding_.is_bound()) {
+    auto request = remote_ns_window_host_binding_.Unbind();
     if (request.is_pending()) {
       mojo::MakeStrongAssociatedBinding(
           std::make_unique<BridgedNativeWidgetHostDummy>(), std::move(request));
@@ -331,47 +331,49 @@
   // destruction.
   // TODO(ccameron): When all communication from |bridge_| to this goes through
   // the BridgedNativeWidgetHost, this can be replaced with closing that pipe.
-  bridge_impl_.reset();
+  in_process_ns_window_bridge_.reset();
   SetFocusManager(nullptr);
   DestroyCompositor();
 }
 
-NativeWidgetMacNSWindow* NativeWidgetMacNSWindowHost::GetLocalNSWindow() const {
-  return local_window_.get();
+NativeWidgetMacNSWindow* NativeWidgetMacNSWindowHost::GetInProcessNSWindow()
+    const {
+  return in_process_ns_window_.get();
 }
 
 gfx::NativeViewAccessible
 NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSView() const {
-  if (bridge_impl_)
-    return bridge_impl_->ns_view();
+  if (in_process_ns_window_bridge_)
+    return in_process_ns_window_bridge_->ns_view();
   return remote_view_accessible_.get();
 }
 
 gfx::NativeViewAccessible
 NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSWindow() const {
-  if (bridge_impl_)
-    return bridge_impl_->ns_window();
+  if (in_process_ns_window_bridge_)
+    return in_process_ns_window_bridge_->ns_window();
   return remote_window_accessible_.get();
 }
 
-remote_cocoa::mojom::NativeWidgetNSWindow* NativeWidgetMacNSWindowHost::bridge()
-    const {
-  if (bridge_ptr_)
-    return bridge_ptr_.get();
-  if (bridge_impl_)
-    return bridge_impl_.get();
+remote_cocoa::mojom::NativeWidgetNSWindow*
+NativeWidgetMacNSWindowHost::GetNSWindowMojo() const {
+  if (remote_ns_window_ptr_)
+    return remote_ns_window_ptr_.get();
+  if (in_process_ns_window_bridge_)
+    return in_process_ns_window_bridge_.get();
   return nullptr;
 }
 
-void NativeWidgetMacNSWindowHost::CreateLocalBridge(
+void NativeWidgetMacNSWindowHost::CreateInProcessNSWindowBridge(
     base::scoped_nsobject<NativeWidgetMacNSWindow> window) {
-  local_window_ = window;
-  bridge_impl_ = std::make_unique<remote_cocoa::NativeWidgetNSWindowBridge>(
-      widget_id_, this, this, text_input_host_.get());
-  bridge_impl_->SetWindow(window);
+  in_process_ns_window_ = window;
+  in_process_ns_window_bridge_ =
+      std::make_unique<remote_cocoa::NativeWidgetNSWindowBridge>(
+          widget_id_, this, this, text_input_host_.get());
+  in_process_ns_window_bridge_->SetWindow(window);
 }
 
-void NativeWidgetMacNSWindowHost::CreateRemoteBridge(
+void NativeWidgetMacNSWindowHost::CreateRemoteNSWindow(
     remote_cocoa::ApplicationHost* application_host,
     remote_cocoa::mojom::CreateWindowParamsPtr window_create_params) {
   accessibility_focus_overrider_.SetAppIsRemote(true);
@@ -381,30 +383,33 @@
   // Create a local invisible window that will be used as the gfx::NativeWindow
   // handle to track this window in this process.
   {
-    auto local_window_create_params =
+    auto in_process_ns_window_create_params =
         remote_cocoa::mojom::CreateWindowParams::New();
-    local_window_create_params->style_mask = NSBorderlessWindowMask;
-    local_window_ = remote_cocoa::NativeWidgetNSWindowBridge::CreateNSWindow(
-        local_window_create_params.get());
-    [local_window_ setBridgedNativeWidgetId:widget_id_];
-    [local_window_ setAlphaValue:0.0];
-    local_view_id_mapping_ =
+    in_process_ns_window_create_params->style_mask = NSBorderlessWindowMask;
+    in_process_ns_window_ =
+        remote_cocoa::NativeWidgetNSWindowBridge::CreateNSWindow(
+            in_process_ns_window_create_params.get());
+    [in_process_ns_window_ setBridgedNativeWidgetId:widget_id_];
+    [in_process_ns_window_ setAlphaValue:0.0];
+    in_process_view_id_mapping_ =
         std::make_unique<remote_cocoa::ScopedNSViewIdMapping>(
-            root_view_id_, [local_window_ contentView]);
+            root_view_id_, [in_process_ns_window_ contentView]);
   }
 
-  // Initialize |bridge_ptr_| to point to a bridge created by |factory|.
+  // Initialize |remote_ns_window_ptr_| to point to a bridge created by
+  // |factory|.
   remote_cocoa::mojom::NativeWidgetNSWindowHostAssociatedPtr host_ptr;
-  host_mojo_binding_.Bind(mojo::MakeRequest(&host_ptr),
-                          ui::WindowResizeHelperMac::Get()->task_runner());
+  remote_ns_window_host_binding_.Bind(
+      mojo::MakeRequest(&host_ptr),
+      ui::WindowResizeHelperMac::Get()->task_runner());
   remote_cocoa::mojom::TextInputHostAssociatedPtr text_input_host_ptr;
   text_input_host_->BindRequest(mojo::MakeRequest(&text_input_host_ptr));
   application_host_->GetApplication()->CreateNativeWidgetNSWindow(
-      widget_id_, mojo::MakeRequest(&bridge_ptr_), host_ptr.PassInterface(),
-      text_input_host_ptr.PassInterface());
+      widget_id_, mojo::MakeRequest(&remote_ns_window_ptr_),
+      host_ptr.PassInterface(), text_input_host_ptr.PassInterface());
 
   // Create the window in its process, and attach it to its parent window.
-  bridge()->CreateWindow(std::move(window_create_params));
+  GetNSWindowMojo()->CreateWindow(std::move(window_create_params));
 }
 
 void NativeWidgetMacNSWindowHost::InitWindow(const Widget::InitParams& params) {
@@ -413,7 +418,7 @@
   // native on Mac, so nothing should ever want one in Widget form.
   DCHECK_NE(params.type, Widget::InitParams::TYPE_TOOLTIP);
   widget_type_ = params.type;
-  tooltip_manager_.reset(new TooltipManagerMac(bridge()));
+  tooltip_manager_.reset(new TooltipManagerMac(GetNSWindowMojo()));
 
   // Initialize the window.
   {
@@ -449,7 +454,7 @@
         widget_type_ == Widget::InitParams::TYPE_WINDOW &&
         params.remove_standard_frame;
 
-    bridge()->InitWindow(std::move(window_params));
+    GetNSWindowMojo()->InitWindow(std::move(window_params));
   }
 
   // Set a meaningful initial bounds. Note that except for frameless widgets
@@ -459,22 +464,22 @@
   // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized
   // (i.e. 1x1) window appearing.
   UpdateLocalWindowFrame(params.bounds);
-  bridge()->SetInitialBounds(params.bounds, widget->GetMinimumSize());
+  GetNSWindowMojo()->SetInitialBounds(params.bounds, widget->GetMinimumSize());
 
-  // TODO(ccameron): Correctly set these based |local_window_|.
+  // TODO(ccameron): Correctly set these based |in_process_ns_window_|.
   window_bounds_in_screen_ = params.bounds;
   content_bounds_in_screen_ = params.bounds;
 
   // Widgets for UI controls (usually layered above web contents) start visible.
   if (widget_type_ == Widget::InitParams::TYPE_CONTROL)
-    bridge()->SetVisibilityState(WindowVisibilityState::kShowInactive);
+    GetNSWindowMojo()->SetVisibilityState(WindowVisibilityState::kShowInactive);
 }
 
 void NativeWidgetMacNSWindowHost::CloseWindowNow() {
-  bool is_out_of_process = !bridge_impl_;
+  bool is_out_of_process = !in_process_ns_window_bridge_;
   // Note that CloseWindowNow may delete |this| for in-process windows.
-  if (bridge())
-    bridge()->CloseWindowNow();
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->CloseWindowNow();
 
   // If it is out-of-process, then simulate the calls that would have been
   // during window closure.
@@ -488,8 +493,8 @@
 
 void NativeWidgetMacNSWindowHost::SetBounds(const gfx::Rect& bounds) {
   UpdateLocalWindowFrame(bounds);
-  bridge()->SetBounds(bounds,
-                      native_widget_mac_->GetWidget()->GetMinimumSize());
+  GetNSWindowMojo()->SetBounds(
+      bounds, native_widget_mac_->GetWidget()->GetMinimumSize());
 }
 
 void NativeWidgetMacNSWindowHost::SetFullscreen(bool fullscreen) {
@@ -499,16 +504,16 @@
   // transition (and therefore OnWindowFullscreenTransitionStart will not be
   // called until the current transition completes).
   target_fullscreen_state_ = fullscreen;
-  bridge()->SetFullscreen(target_fullscreen_state_);
+  GetNSWindowMojo()->SetFullscreen(target_fullscreen_state_);
 }
 
 void NativeWidgetMacNSWindowHost::SetRootView(views::View* root_view) {
   root_view_ = root_view;
   if (root_view_) {
     // TODO(ccameron): Drag-drop functionality does not yet run over mojo.
-    if (bridge_impl_) {
-      drag_drop_client_.reset(
-          new DragDropClientMac(bridge_impl_.get(), root_view_));
+    if (in_process_ns_window_bridge_) {
+      drag_drop_client_.reset(new DragDropClientMac(
+          in_process_ns_window_bridge_.get(), root_view_));
     }
   } else {
     drag_drop_client_.reset();
@@ -550,7 +555,7 @@
   if (is_visible_)
     compositor_->Unsuspend();
 
-  bridge()->InitCompositorView();
+  GetNSWindowMojo()->InitCompositorView();
 }
 
 void NativeWidgetMacNSWindowHost::UpdateCompositorProperties() {
@@ -606,7 +611,7 @@
   if (window_title_ == title)
     return false;
   window_title_ = title;
-  bridge()->SetWindowTitle(window_title_);
+  GetNSWindowMojo()->SetWindowTitle(window_title_);
   return true;
 }
 
@@ -619,12 +624,12 @@
 bool NativeWidgetMacNSWindowHost::RedispatchKeyEvent(NSEvent* event) {
   // If the target window is in-process, then redispatch the event directly,
   // and give an accurate return value.
-  if (bridge_impl_)
-    return bridge_impl_->RedispatchKeyEvent(event);
+  if (in_process_ns_window_bridge_)
+    return in_process_ns_window_bridge_->RedispatchKeyEvent(event);
 
   // If the target window is out of process then always report the event as
   // handled (because it should never be handled in this process).
-  bridge()->RedispatchKeyEvent(
+  GetNSWindowMojo()->RedispatchKeyEvent(
       [event type], [event modifierFlags], [event timestamp],
       base::SysNSStringToUTF16([event characters]),
       base::SysNSStringToUTF16([event charactersIgnoringModifiers]),
@@ -686,16 +691,16 @@
   if (new_application_host != application_host_) {
     DLOG(ERROR) << "Cannot migrate views::NativeWidget to another process, "
                    "closing it instead.";
-    bridge()->CloseWindow();
+    GetNSWindowMojo()->CloseWindow();
     return;
   }
 
   parent_ = new_parent;
   if (parent_) {
     parent_->children_.push_back(this);
-    bridge()->SetParent(parent_->bridged_native_widget_id());
+    GetNSWindowMojo()->SetParent(parent_->bridged_native_widget_id());
   } else {
-    bridge()->SetParent(0);
+    GetNSWindowMojo()->SetParent(0);
   }
 }
 
@@ -718,8 +723,8 @@
     return;
   std::map<NSView*, int> rank;
   RankNSViewsRecursive(widget->GetRootView(), &rank);
-  if (bridge_impl_)
-    bridge_impl_->SortSubviews(std::move(rank));
+  if (in_process_ns_window_bridge_)
+    in_process_ns_window_bridge_->SortSubviews(std::move(rank));
 }
 
 void NativeWidgetMacNSWindowHost::RankNSViewsRecursive(
@@ -734,9 +739,11 @@
 
 void NativeWidgetMacNSWindowHost::UpdateLocalWindowFrame(
     const gfx::Rect& frame) {
-  if (!bridge_ptr_)
+  if (!remote_ns_window_ptr_)
     return;
-  [local_window_ setFrame:gfx::ScreenRectToNSRect(frame) display:NO animate:NO];
+  [in_process_ns_window_ setFrame:gfx::ScreenRectToNSRect(frame)
+                          display:NO
+                          animate:NO];
 }
 
 // static
@@ -1380,7 +1387,7 @@
 void NativeWidgetMacNSWindowHost::OnDialogChanged() {
   // Note it's only necessary to clear the TouchBar. If the OS needs it again,
   // a new one will be created.
-  bridge()->ClearTouchBar();
+  GetNSWindowMojo()->ClearTouchBar();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1458,7 +1465,7 @@
     // mach port has been specified (in practice, when software compositing is
     // enabled).
     // https://crbug.com/942213
-    if (bridge_ptr_ && ca_layer_params->io_surface_mach_port) {
+    if (remote_ns_window_ptr_ && ca_layer_params->io_surface_mach_port) {
       gfx::CALayerParams updated_ca_layer_params = *ca_layer_params;
       if (!io_surface_to_remote_layer_interceptor_) {
         io_surface_to_remote_layer_interceptor_ =
@@ -1466,9 +1473,9 @@
       }
       io_surface_to_remote_layer_interceptor_->UpdateCALayerParams(
           &updated_ca_layer_params);
-      bridge_ptr_->SetCALayerParams(updated_ca_layer_params);
+      remote_ns_window_ptr_->SetCALayerParams(updated_ca_layer_params);
     } else {
-      bridge()->SetCALayerParams(*ca_layer_params);
+      GetNSWindowMojo()->SetCALayerParams(*ca_layer_params);
     }
   }
 
diff --git a/ui/views/cocoa/text_input_host.mm b/ui/views/cocoa/text_input_host.mm
index 22a29b7..2af42b5 100644
--- a/ui/views/cocoa/text_input_host.mm
+++ b/ui/views/cocoa/text_input_host.mm
@@ -175,8 +175,8 @@
   text_input_client_ = new_text_input_client;
   pending_text_input_client_ = new_text_input_client;
 
-  if (host_impl_->bridge_impl_ &&
-      host_impl_->bridge_impl_->NeedsUpdateWindows()) {
+  if (host_impl_->in_process_ns_window_bridge_ &&
+      host_impl_->in_process_ns_window_bridge_->NeedsUpdateWindows()) {
     text_input_client_ = old_text_input_client;
     [NSApp updateWindows];
     // Note: |pending_text_input_client_| (and therefore +[NSTextInputContext
diff --git a/ui/views/controls/native/native_view_host_mac.h b/ui/views/controls/native/native_view_host_mac.h
index 2bd4dbf..01b34f7 100644
--- a/ui/views/controls/native/native_view_host_mac.h
+++ b/ui/views/controls/native/native_view_host_mac.h
@@ -57,7 +57,7 @@
 
  private:
   // Return the NativeWidgetMacNSWindowHost for this hosted view.
-  NativeWidgetMacNSWindowHost* GetBridgedNativeWidgetHost() const;
+  NativeWidgetMacNSWindowHost* GetNSWindowHost() const;
 
   // Our associated NativeViewHost. Owns this.
   NativeViewHost* host_;
diff --git a/ui/views/controls/native/native_view_host_mac.mm b/ui/views/controls/native/native_view_host_mac.mm
index 8182eba..be0e09c 100644
--- a/ui/views/controls/native/native_view_host_mac.mm
+++ b/ui/views/controls/native/native_view_host_mac.mm
@@ -40,8 +40,7 @@
 NativeViewHostMac::~NativeViewHostMac() {
 }
 
-NativeWidgetMacNSWindowHost* NativeViewHostMac::GetBridgedNativeWidgetHost()
-    const {
+NativeWidgetMacNSWindowHost* NativeViewHostMac::GetNSWindowHost() const {
   return NativeWidgetMacNSWindowHost::GetFromNativeWindow(
       host_->GetWidget()->GetNativeWindow());
 }
@@ -55,17 +54,17 @@
 
 remote_cocoa::mojom::Application* NativeViewHostMac::GetRemoteCocoaApplication()
     const {
-  if (auto* bridge_host = GetBridgedNativeWidgetHost()) {
-    if (auto* application_host = bridge_host->application_host())
+  if (auto* window_host = GetNSWindowHost()) {
+    if (auto* application_host = window_host->application_host())
       return application_host->GetApplication();
   }
   return nullptr;
 }
 
 uint64_t NativeViewHostMac::GetNSViewId() const {
-  auto* bridge_host = GetBridgedNativeWidgetHost();
-  if (bridge_host)
-    return bridge_host->GetRootViewNSViewId();
+  auto* window_host = GetNSWindowHost();
+  if (window_host)
+    return window_host->GetRootViewNSViewId();
   return 0;
 }
 
@@ -88,13 +87,13 @@
   }
   EnsureNativeViewHasNoChildWidgets(native_view_);
 
-  auto* bridge_host = GetBridgedNativeWidgetHost();
-  CHECK(bridge_host);
+  auto* window_host = GetNSWindowHost();
+  CHECK(window_host);
 
   // TODO(https://crbug.com/933679): This is lifted out the ViewsHostableAttach
   // call below because of crashes being observed in the field.
   NSView* superview =
-      bridge_host->native_widget_mac()->GetNativeView().GetNativeNSView();
+      window_host->native_widget_mac()->GetNativeView().GetNativeNSView();
   [superview addSubview:native_view_];
 
   if (native_view_hostable_) {
@@ -105,7 +104,7 @@
         host_->parent()->GetNativeViewAccessible());
   }
 
-  bridge_host->SetAssociationForView(host_, native_view_);
+  window_host->SetAssociationForView(host_, native_view_);
 }
 
 void NativeViewHostMac::NativeViewDetaching(bool destroyed) {
@@ -131,10 +130,10 @@
   }
 
   EnsureNativeViewHasNoChildWidgets(native_view_);
-  auto* bridge_host = GetBridgedNativeWidgetHost();
+  auto* window_host = GetNSWindowHost();
   // NativeWidgetNSWindowBridge can be null when Widget is closing.
-  if (bridge_host)
-    bridge_host->ClearAssociationForView(host_);
+  if (window_host)
+    window_host->ClearAssociationForView(host_);
 
   native_view_.reset();
 }
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h
index 23848a8..c03b37c 100644
--- a/ui/views/widget/native_widget_mac.h
+++ b/ui/views/widget/native_widget_mac.h
@@ -208,10 +208,16 @@
   virtual void OnWindowDestroying(gfx::NativeWindow window) {}
 
   internal::NativeWidgetDelegate* delegate() { return delegate_; }
-  remote_cocoa::mojom::NativeWidgetNSWindow* bridge() const;
-  remote_cocoa::NativeWidgetNSWindowBridge* bridge_impl() const;
-  NativeWidgetMacNSWindowHost* bridge_host() const {
-    return bridge_host_.get();
+
+  // Return the mojo interface for the NSWindow. The interface may be
+  // implemented in-process or out-of-process.
+  remote_cocoa::mojom::NativeWidgetNSWindow* GetNSWindowMojo() const;
+
+  // Return the bridge structure only if this widget is in-process.
+  remote_cocoa::NativeWidgetNSWindowBridge* GetInProcessNSWindowBridge() const;
+
+  NativeWidgetMacNSWindowHost* GetNSWindowHost() const {
+    return ns_window_host_.get();
   }
 
  private:
@@ -220,7 +226,7 @@
   friend class views::test::WidgetTest;
 
   internal::NativeWidgetDelegate* delegate_;
-  std::unique_ptr<NativeWidgetMacNSWindowHost> bridge_host_;
+  std::unique_ptr<NativeWidgetMacNSWindowHost> ns_window_host_;
 
   Widget::InitParams::Ownership ownership_;
 
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index dcdaed2..4a441b8 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -72,7 +72,7 @@
 
 NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate)
     : delegate_(delegate),
-      bridge_host_(new NativeWidgetMacNSWindowHost(this)),
+      ns_window_host_(new NativeWidgetMacNSWindowHost(this)),
       ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {}
 
 NativeWidgetMac::~NativeWidgetMac() {
@@ -88,8 +88,8 @@
 }
 
 void NativeWidgetMac::WindowDestroyed() {
-  DCHECK(bridge());
-  bridge_host_.reset();
+  DCHECK(GetNSWindowMojo());
+  ns_window_host_.reset();
   // |OnNativeWidgetDestroyed| may delete |this| if the object does not own
   // itself.
   bool should_delete_this =
@@ -143,15 +143,15 @@
   PopulateCreateWindowParams(params, create_window_params.get());
 
   if (application_host) {
-    bridge_host_->CreateRemoteBridge(application_host,
-                                     std::move(create_window_params));
+    ns_window_host_->CreateRemoteNSWindow(application_host,
+                                          std::move(create_window_params));
   } else {
     base::scoped_nsobject<NativeWidgetMacNSWindow> window(
         [CreateNSWindow(create_window_params.get()) retain]);
-    bridge_host_->CreateLocalBridge(std::move(window));
+    ns_window_host_->CreateInProcessNSWindowBridge(std::move(window));
   }
-  bridge_host_->SetParent(parent_host);
-  bridge_host_->InitWindow(params);
+  ns_window_host_->SetParent(parent_host);
+  ns_window_host_->InitWindow(params);
   OnWindowInitialized();
 
   // Only set always-on-top here if it is true since setting it may affect how
@@ -162,15 +162,15 @@
   delegate_->OnNativeWidgetCreated();
 
   DCHECK(GetWidget()->GetRootView());
-  bridge_host_->SetRootView(GetWidget()->GetRootView());
-  bridge()->CreateContentView(bridge_host_->GetRootViewNSViewId(),
-                              GetWidget()->GetRootView()->bounds());
+  ns_window_host_->SetRootView(GetWidget()->GetRootView());
+  GetNSWindowMojo()->CreateContentView(ns_window_host_->GetRootViewNSViewId(),
+                                       GetWidget()->GetRootView()->bounds());
   if (auto* focus_manager = GetWidget()->GetFocusManager()) {
-    bridge()->MakeFirstResponder();
-    bridge_host_->SetFocusManager(focus_manager);
+    GetNSWindowMojo()->MakeFirstResponder();
+    ns_window_host_->SetFocusManager(focus_manager);
   }
 
-  bridge_host_->CreateCompositor(params);
+  ns_window_host_->CreateCompositor(params);
 
   if (g_init_native_widget_callback)
     g_init_native_widget_callback->Run(this);
@@ -178,7 +178,7 @@
 
 void NativeWidgetMac::OnWidgetInitDone() {
   OnSizeConstraintsChanged();
-  bridge_host_->OnWidgetInitDone();
+  ns_window_host_->OnWidgetInitDone();
 }
 
 NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() {
@@ -216,7 +216,7 @@
 }
 
 gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const {
-  return bridge_host_ ? bridge_host_->GetLocalNSWindow() : nil;
+  return ns_window_host_ ? ns_window_host_->GetInProcessNSWindow() : nil;
 }
 
 Widget* NativeWidgetMac::GetTopLevelWidget() {
@@ -225,66 +225,66 @@
 }
 
 const ui::Compositor* NativeWidgetMac::GetCompositor() const {
-  return bridge_host_ && bridge_host_->layer()
-             ? bridge_host_->layer()->GetCompositor()
+  return ns_window_host_ && ns_window_host_->layer()
+             ? ns_window_host_->layer()->GetCompositor()
              : nullptr;
 }
 
 const ui::Layer* NativeWidgetMac::GetLayer() const {
-  return bridge_host_ ? bridge_host_->layer() : nullptr;
+  return ns_window_host_ ? ns_window_host_->layer() : nullptr;
 }
 
 void NativeWidgetMac::ReorderNativeViews() {
-  if (bridge_host_)
-    bridge_host_->ReorderChildViews();
+  if (ns_window_host_)
+    ns_window_host_->ReorderChildViews();
 }
 
 void NativeWidgetMac::ViewRemoved(View* view) {
   DragDropClientMac* client =
-      bridge_host_ ? bridge_host_->drag_drop_client() : nullptr;
+      ns_window_host_ ? ns_window_host_->drag_drop_client() : nullptr;
   if (client)
     client->drop_helper()->ResetTargetViewIfEquals(view);
 }
 
 void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) {
-  if (bridge_host_)
-    bridge_host_->SetNativeWindowProperty(name, value);
+  if (ns_window_host_)
+    ns_window_host_->SetNativeWindowProperty(name, value);
 }
 
 void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const {
-  if (bridge_host_)
-    return bridge_host_->GetNativeWindowProperty(name);
+  if (ns_window_host_)
+    return ns_window_host_->GetNativeWindowProperty(name);
 
   return nullptr;
 }
 
 TooltipManager* NativeWidgetMac::GetTooltipManager() const {
-  if (bridge_host_)
-    return bridge_host_->tooltip_manager();
+  if (ns_window_host_)
+    return ns_window_host_->tooltip_manager();
 
   return nullptr;
 }
 
 void NativeWidgetMac::SetCapture() {
-  if (bridge())
-    bridge()->AcquireCapture();
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->AcquireCapture();
 }
 
 void NativeWidgetMac::ReleaseCapture() {
-  if (bridge())
-    bridge()->ReleaseCapture();
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->ReleaseCapture();
 }
 
 bool NativeWidgetMac::HasCapture() const {
-  return bridge_host_ && bridge_host_->IsMouseCaptureActive();
+  return ns_window_host_ && ns_window_host_->IsMouseCaptureActive();
 }
 
 ui::InputMethod* NativeWidgetMac::GetInputMethod() {
-  return bridge_host_ ? bridge_host_->GetInputMethod() : nullptr;
+  return ns_window_host_ ? ns_window_host_->GetInputMethod() : nullptr;
 }
 
 void NativeWidgetMac::CenterWindow(const gfx::Size& size) {
-  bridge()->SetSizeAndCenter(size, GetWidget()->GetMinimumSize());
+  GetNSWindowMojo()->SetSizeAndCenter(size, GetWidget()->GetMinimumSize());
 }
 
 void NativeWidgetMac::GetWindowPlacement(
@@ -300,9 +300,9 @@
 }
 
 bool NativeWidgetMac::SetWindowTitle(const base::string16& title) {
-  if (!bridge_host_)
+  if (!ns_window_host_)
     return false;
-  return bridge_host_->SetWindowTitle(title);
+  return ns_window_host_->SetWindowTitle(title);
 }
 
 void NativeWidgetMac::SetWindowIcons(const gfx::ImageSkia& window_icon,
@@ -322,21 +322,23 @@
   // A peculiarity of the constrained window framework is that it permits a
   // dialog of MODAL_TYPE_WINDOW to have a null parent window; falling back to
   // a non-modal window in this case.
-  DCHECK(bridge_host_->parent() || modal_type == ui::MODAL_TYPE_WINDOW);
+  DCHECK(ns_window_host_->parent() || modal_type == ui::MODAL_TYPE_WINDOW);
 
   // Everything happens upon show.
 }
 
 gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const {
-  return bridge_host_ ? bridge_host_->GetWindowBoundsInScreen() : gfx::Rect();
+  return ns_window_host_ ? ns_window_host_->GetWindowBoundsInScreen()
+                         : gfx::Rect();
 }
 
 gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const {
-  return bridge_host_ ? bridge_host_->GetContentBoundsInScreen() : gfx::Rect();
+  return ns_window_host_ ? ns_window_host_->GetContentBoundsInScreen()
+                         : gfx::Rect();
 }
 
 gfx::Rect NativeWidgetMac::GetRestoredBounds() const {
-  return bridge_host_ ? bridge_host_->GetRestoredBounds() : gfx::Rect();
+  return ns_window_host_ ? ns_window_host_->GetRestoredBounds() : gfx::Rect();
 }
 
 std::string NativeWidgetMac::GetWorkspace() const {
@@ -344,17 +346,17 @@
 }
 
 void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) {
-  if (bridge_host_)
-    bridge_host_->SetBounds(bounds);
+  if (ns_window_host_)
+    ns_window_host_->SetBounds(bounds);
 }
 
 void NativeWidgetMac::SetBoundsConstrained(const gfx::Rect& bounds) {
-  if (!bridge_host_)
+  if (!ns_window_host_)
     return;
   gfx::Rect new_bounds(bounds);
-  if (bridge_host_->parent()) {
+  if (ns_window_host_->parent()) {
     new_bounds.AdjustToFit(
-        gfx::Rect(bridge_host_->parent()->GetWindowBoundsInScreen().size()));
+        gfx::Rect(ns_window_host_->parent()->GetWindowBoundsInScreen().size()));
   } else {
     new_bounds = ConstrainBoundsToDisplayWorkArea(new_bounds);
   }
@@ -368,7 +370,7 @@
 }
 
 void NativeWidgetMac::StackAbove(gfx::NativeView native_view) {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
 
   auto* sibling_host =
@@ -376,17 +378,17 @@
 
   if (!sibling_host) {
     // This will only work if |this| is in-process.
-    DCHECK(!bridge_host_->application_host());
+    DCHECK(!ns_window_host_->application_host());
     NSInteger view_parent = native_view.GetNativeNSView().window.windowNumber;
     [GetNativeWindow().GetNativeNSWindow() orderWindow:NSWindowAbove
                                             relativeTo:view_parent];
     return;
   }
 
-  if (bridge_host_->application_host() == sibling_host->application_host()) {
+  if (ns_window_host_->application_host() == sibling_host->application_host()) {
     // Check if |native_view|'s NativeWidgetMacNSWindowHost corresponds to the
     // same process as |this|.
-    bridge()->StackAbove(sibling_host->bridged_native_widget_id());
+    GetNSWindowMojo()->StackAbove(sibling_host->bridged_native_widget_id());
     return;
   }
 
@@ -395,8 +397,8 @@
 }
 
 void NativeWidgetMac::StackAtTop() {
-  if (bridge())
-    bridge()->StackAtTop();
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->StackAtTop();
 }
 
 void NativeWidgetMac::SetShape(std::unique_ptr<Widget::ShapeRects> shape) {
@@ -404,20 +406,20 @@
 }
 
 void NativeWidgetMac::Close() {
-  if (bridge())
-    bridge()->CloseWindow();
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->CloseWindow();
 }
 
 void NativeWidgetMac::CloseNow() {
-  if (bridge_host_)
-    bridge_host_->CloseWindowNow();
-  // Note: |bridge_host_| will be deleted here, and |this| will be deleted here
-  // when ownership_ == NATIVE_WIDGET_OWNS_WIDGET,
+  if (ns_window_host_)
+    ns_window_host_->CloseWindowNow();
+  // Note: |ns_window_host_| will be deleted here, and |this| will be deleted
+  // here when ownership_ == NATIVE_WIDGET_OWNS_WIDGET,
 }
 
 void NativeWidgetMac::Show(ui::WindowShowState show_state,
                            const gfx::Rect& restore_bounds) {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
 
   switch (show_state) {
@@ -439,7 +441,7 @@
     window_state = WindowVisibilityState::kShowInactive;
   else if (show_state == ui::SHOW_STATE_MINIMIZED)
     window_state = WindowVisibilityState::kHideWindow;
-  bridge()->SetVisibilityState(window_state);
+  GetNSWindowMojo()->SetVisibilityState(window_state);
 
   // Ignore the SetInitialFocus() result. BridgedContentView should get
   // firstResponder status regardless.
@@ -447,19 +449,20 @@
 }
 
 void NativeWidgetMac::Hide() {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->SetVisibilityState(WindowVisibilityState::kHideWindow);
+  GetNSWindowMojo()->SetVisibilityState(WindowVisibilityState::kHideWindow);
 }
 
 bool NativeWidgetMac::IsVisible() const {
-  return bridge_host_ && bridge_host_->IsVisible();
+  return ns_window_host_ && ns_window_host_->IsVisible();
 }
 
 void NativeWidgetMac::Activate() {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->SetVisibilityState(WindowVisibilityState::kShowAndActivateWindow);
+  GetNSWindowMojo()->SetVisibilityState(
+      WindowVisibilityState::kShowAndActivateWindow);
 }
 
 void NativeWidgetMac::Deactivate() {
@@ -467,7 +470,7 @@
 }
 
 bool NativeWidgetMac::IsActive() const {
-  return bridge_host_ ? bridge_host_->IsWindowKey() : false;
+  return ns_window_host_ ? ns_window_host_->IsWindowKey() : false;
 }
 
 void NativeWidgetMac::SetAlwaysOnTop(bool always_on_top) {
@@ -480,9 +483,9 @@
 }
 
 void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->SetVisibleOnAllSpaces(always_visible);
+  GetNSWindowMojo()->SetVisibleOnAllSpaces(always_visible);
 }
 
 bool NativeWidgetMac::IsVisibleOnAllWorkspaces() const {
@@ -494,9 +497,9 @@
 }
 
 void NativeWidgetMac::Minimize() {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->SetMiniaturized(true);
+  GetNSWindowMojo()->SetMiniaturized(true);
 }
 
 bool NativeWidgetMac::IsMaximized() const {
@@ -506,46 +509,46 @@
 }
 
 bool NativeWidgetMac::IsMinimized() const {
-  if (!bridge_host_)
+  if (!ns_window_host_)
     return false;
-  return bridge_host_->IsMiniaturized();
+  return ns_window_host_->IsMiniaturized();
 }
 
 void NativeWidgetMac::Restore() {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->SetFullscreen(false);
-  bridge()->SetMiniaturized(false);
+  GetNSWindowMojo()->SetFullscreen(false);
+  GetNSWindowMojo()->SetMiniaturized(false);
 }
 
 void NativeWidgetMac::SetFullscreen(bool fullscreen) {
-  if (!bridge_host_)
+  if (!ns_window_host_)
     return;
-  bridge_host_->SetFullscreen(fullscreen);
+  ns_window_host_->SetFullscreen(fullscreen);
 }
 
 bool NativeWidgetMac::IsFullscreen() const {
-  return bridge_host_ && bridge_host_->target_fullscreen_state();
+  return ns_window_host_ && ns_window_host_->target_fullscreen_state();
 }
 
 void NativeWidgetMac::SetCanAppearInExistingFullscreenSpaces(
     bool can_appear_in_existing_fullscreen_spaces) {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->SetCanAppearInExistingFullscreenSpaces(
+  GetNSWindowMojo()->SetCanAppearInExistingFullscreenSpaces(
       can_appear_in_existing_fullscreen_spaces);
 }
 
 void NativeWidgetMac::SetOpacity(float opacity) {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->SetOpacity(opacity);
+  GetNSWindowMojo()->SetOpacity(opacity);
 }
 
 void NativeWidgetMac::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->SetContentAspectRatio(aspect_ratio);
+  GetNSWindowMojo()->SetContentAspectRatio(aspect_ratio);
 }
 
 void NativeWidgetMac::FlashFrame(bool flash_frame) {
@@ -557,8 +560,8 @@
                                    const gfx::Point& location,
                                    int operation,
                                    ui::DragDropTypes::DragEventSource source) {
-  bridge_host_->drag_drop_client()->StartDragAndDrop(view, data, operation,
-                                                     source);
+  ns_window_host_->drag_drop_client()->StartDragAndDrop(view, data, operation,
+                                                        source);
 }
 
 void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) {
@@ -571,8 +574,8 @@
   target_rect.origin.y =
       NSHeight(client_rect) - target_rect.origin.y - NSHeight(target_rect);
   [GetNativeView().GetNativeNSView() setNeedsDisplayInRect:target_rect];
-  if (bridge_host_ && bridge_host_->layer())
-    bridge_host_->layer()->SchedulePaint(rect);
+  if (ns_window_host_ && ns_window_host_->layer())
+    ns_window_host_->layer()->SchedulePaint(rect);
 }
 
 void NativeWidgetMac::ScheduleLayout() {
@@ -582,15 +585,15 @@
 }
 
 void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) {
-  if (bridge_impl())
-    bridge_impl()->SetCursor(cursor);
+  if (GetInProcessNSWindowBridge())
+    GetInProcessNSWindowBridge()->SetCursor(cursor);
 }
 
 void NativeWidgetMac::ShowEmojiPanel() {
   // We must plumb the call to ui::ShowEmojiPanel() over the bridge so that it
   // is called from the correct process.
-  if (bridge())
-    bridge()->ShowEmojiPanel();
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->ShowEmojiPanel();
 }
 
 bool NativeWidgetMac::IsMouseEventsEnabled() const {
@@ -608,36 +611,37 @@
   // To quote DesktopWindowTreeHostX11, "This method is weird and misnamed."
   // The goal is to set focus to the content window, thereby removing focus from
   // any NSView in the window that doesn't belong to toolkit-views.
-  if (!bridge())
+  if (!GetNSWindowMojo())
     return;
-  bridge()->MakeFirstResponder();
+  GetNSWindowMojo()->MakeFirstResponder();
 }
 
 gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const {
-  return bridge_host_ ? bridge_host_->GetCurrentDisplay().work_area()
-                      : gfx::Rect();
+  return ns_window_host_ ? ns_window_host_->GetCurrentDisplay().work_area()
+                         : gfx::Rect();
 }
 
 Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop(
     const gfx::Vector2d& drag_offset,
     Widget::MoveLoopSource source,
     Widget::MoveLoopEscapeBehavior escape_behavior) {
-  if (!bridge_impl())
+  if (!GetInProcessNSWindowBridge())
     return Widget::MOVE_LOOP_CANCELED;
 
   ReleaseCapture();
-  return bridge_impl()->RunMoveLoop(drag_offset) ? Widget::MOVE_LOOP_SUCCESSFUL
-                                                 : Widget::MOVE_LOOP_CANCELED;
+  return GetInProcessNSWindowBridge()->RunMoveLoop(drag_offset)
+             ? Widget::MOVE_LOOP_SUCCESSFUL
+             : Widget::MOVE_LOOP_CANCELED;
 }
 
 void NativeWidgetMac::EndMoveLoop() {
-  if (bridge_impl())
-    bridge_impl()->EndMoveLoop();
+  if (GetInProcessNSWindowBridge())
+    GetInProcessNSWindowBridge()->EndMoveLoop();
 }
 
 void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) {
-  if (bridge())
-    bridge()->SetAnimationEnabled(value);
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->SetAnimationEnabled(value);
 }
 
 void NativeWidgetMac::SetVisibilityAnimationDuration(
@@ -663,8 +667,8 @@
       transitions = remote_cocoa::mojom::VisibilityTransition::kBoth;
       break;
   }
-  if (bridge())
-    bridge()->SetTransitionsToAnimate(transitions);
+  if (GetNSWindowMojo())
+    GetNSWindowMojo()->SetTransitionsToAnimate(transitions);
 }
 
 bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const {
@@ -677,10 +681,10 @@
 
 void NativeWidgetMac::OnSizeConstraintsChanged() {
   Widget* widget = GetWidget();
-  bridge()->SetSizeConstraints(widget->GetMinimumSize(),
-                               widget->GetMaximumSize(),
-                               widget->widget_delegate()->CanResize(),
-                               widget->widget_delegate()->CanMaximize());
+  GetNSWindowMojo()->SetSizeConstraints(
+      widget->GetMinimumSize(), widget->GetMaximumSize(),
+      widget->widget_delegate()->CanResize(),
+      widget->widget_delegate()->CanMaximize());
 }
 
 std::string NativeWidgetMac::GetName() const {
@@ -716,12 +720,15 @@
   return nullptr;
 }
 
-remote_cocoa::mojom::NativeWidgetNSWindow* NativeWidgetMac::bridge() const {
-  return bridge_host_ ? bridge_host_->bridge() : nullptr;
+remote_cocoa::mojom::NativeWidgetNSWindow* NativeWidgetMac::GetNSWindowMojo()
+    const {
+  return ns_window_host_ ? ns_window_host_->GetNSWindowMojo() : nullptr;
 }
 
-remote_cocoa::NativeWidgetNSWindowBridge* NativeWidgetMac::bridge_impl() const {
-  return bridge_host_ ? bridge_host_->bridge_impl() : nullptr;
+remote_cocoa::NativeWidgetNSWindowBridge*
+NativeWidgetMac::GetInProcessNSWindowBridge() const {
+  return ns_window_host_ ? ns_window_host_->GetInProcessNSWindowBridge()
+                         : nullptr;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -778,9 +785,9 @@
 // static
 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
     gfx::NativeWindow window) {
-  if (NativeWidgetMacNSWindowHost* bridge_host_impl =
+  if (NativeWidgetMacNSWindowHost* ns_window_host_impl =
           NativeWidgetMacNSWindowHost::GetFromNativeWindow(window)) {
-    return bridge_host_impl->native_widget_mac();
+    return ns_window_host_impl->native_widget_mac();
   }
   return nullptr;  // Not created by NativeWidgetMac.
 }
@@ -788,21 +795,21 @@
 // static
 NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
     gfx::NativeView native_view) {
-  NativeWidgetMacNSWindowHost* bridge_host =
+  NativeWidgetMacNSWindowHost* window_host =
       NativeWidgetMacNSWindowHost::GetFromNativeView(native_view);
-  if (!bridge_host)
+  if (!window_host)
     return nullptr;
-  while (bridge_host->parent())
-    bridge_host = bridge_host->parent();
-  return bridge_host->native_widget_mac();
+  while (window_host->parent())
+    window_host = window_host->parent();
+  return window_host->native_widget_mac();
 }
 
 // static
 void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
                                              Widget::Widgets* children) {
-  NativeWidgetMacNSWindowHost* bridge_host =
+  NativeWidgetMacNSWindowHost* window_host =
       NativeWidgetMacNSWindowHost::GetFromNativeView(native_view);
-  if (!bridge_host) {
+  if (!window_host) {
     NSView* ns_view = native_view.GetNativeNSView();
     // The NSWindow is not itself a views::Widget, but it may have children that
     // are. Support returning Widgets that are parented to the NSWindow, except:
@@ -826,37 +833,37 @@
 
   // If |native_view| is a subview of the contentView, it will share an
   // NSWindow, but will itself be a native child of the Widget. That is, adding
-  // bridge_host->..->GetWidget() to |children| would be adding the _parent_ of
+  // window_host->..->GetWidget() to |children| would be adding the _parent_ of
   // |native_view|, not the Widget for |native_view|. |native_view| doesn't have
   // a corresponding Widget of its own in this case (and so can't have Widget
   // children of its own on Mac).
-  if (bridge_host->native_widget_mac()->GetNativeView() != native_view)
+  if (window_host->native_widget_mac()->GetNativeView() != native_view)
     return;
 
   // Code expects widget for |native_view| to be added to |children|.
-  if (bridge_host->native_widget_mac()->GetWidget())
-    children->insert(bridge_host->native_widget_mac()->GetWidget());
+  if (window_host->native_widget_mac()->GetWidget())
+    children->insert(window_host->native_widget_mac()->GetWidget());
 
   // When the NSWindow *is* a Widget, only consider children(). I.e. do not
-  // look through -[NSWindow childWindows] as done for the (!bridge_host) case
+  // look through -[NSWindow childWindows] as done for the (!window_host) case
   // above. -childWindows does not support hidden windows, and anything in there
   // which is not in children() would have been added by AppKit.
-  for (NativeWidgetMacNSWindowHost* child : bridge_host->children())
+  for (NativeWidgetMacNSWindowHost* child : window_host->children())
     GetAllChildWidgets(child->native_widget_mac()->GetNativeView(), children);
 }
 
 // static
 void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view,
                                              Widget::Widgets* owned) {
-  NativeWidgetMacNSWindowHost* bridge_host =
+  NativeWidgetMacNSWindowHost* window_host =
       NativeWidgetMacNSWindowHost::GetFromNativeView(native_view);
-  if (!bridge_host) {
+  if (!window_host) {
     GetAllChildWidgets(native_view, owned);
     return;
   }
-  if (bridge_host->native_widget_mac()->GetNativeView() != native_view)
+  if (window_host->native_widget_mac()->GetNativeView() != native_view)
     return;
-  for (NativeWidgetMacNSWindowHost* child : bridge_host->children())
+  for (NativeWidgetMacNSWindowHost* child : window_host->children())
     GetAllChildWidgets(child->native_widget_mac()->GetNativeView(), owned);
 }
 
@@ -871,25 +878,25 @@
     return;
   }
 
-  NativeWidgetMacNSWindowHost* bridge_host =
+  NativeWidgetMacNSWindowHost* window_host =
       NativeWidgetMacNSWindowHost::GetFromNativeView(native_view);
-  DCHECK(bridge_host);
+  DCHECK(window_host);
   gfx::NativeView bridge_view =
-      bridge_host->native_widget_mac()->GetNativeView();
+      window_host->native_widget_mac()->GetNativeView();
   gfx::NativeWindow bridge_window =
-      bridge_host->native_widget_mac()->GetNativeWindow();
+      window_host->native_widget_mac()->GetNativeWindow();
   bool bridge_is_top_level =
-      bridge_host->native_widget_mac()->GetWidget()->is_top_level();
+      window_host->native_widget_mac()->GetWidget()->is_top_level();
   DCHECK([native_view.GetNativeNSView()
       isDescendantOf:bridge_view.GetNativeNSView()]);
   DCHECK(bridge_window && ![bridge_window.GetNativeNSWindow() isSheet]);
 
-  NativeWidgetMacNSWindowHost* parent_bridge_host =
+  NativeWidgetMacNSWindowHost* parent_window_host =
       NativeWidgetMacNSWindowHost::GetFromNativeView(new_parent);
 
   // Early out for no-op changes.
   if (native_view == bridge_view && bridge_is_top_level &&
-      bridge_host->parent() == parent_bridge_host) {
+      window_host->parent() == parent_window_host) {
     return;
   }
 
@@ -903,11 +910,11 @@
   // Update |brige_host|'s parent only if
   // NativeWidgetNSWindowBridge::ReparentNativeView will.
   if (native_view == bridge_view) {
-    bridge_host->SetParent(parent_bridge_host);
+    window_host->SetParent(parent_window_host);
     if (!bridge_is_top_level) {
-      // Make |bridge_host|'s NSView be a child of |new_parent| by adding it as
+      // Make |window_host|'s NSView be a child of |new_parent| by adding it as
       // a subview. Note that this will have the effect of removing
-      // |bridge_host|'s NSView from its NSWindow. The |NSWindow| must remain
+      // |window_host|'s NSView from its NSWindow. The |NSWindow| must remain
       // visible because it controls the bounds and visibility of the ui::Layer,
       // so just hide it by setting alpha value to zero.
       // TODO(ccameron): This path likely violates assumptions. Verify that this
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 4d77e6a..5974ae32f 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -96,8 +96,8 @@
 class BridgedNativeWidgetTestApi {
  public:
   explicit BridgedNativeWidgetTestApi(NSWindow* window) {
-    bridge_ =
-        NativeWidgetMacNSWindowHost::GetFromNativeWindow(window)->bridge_impl();
+    bridge_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow(window)
+                  ->GetInProcessNSWindowBridge();
   }
 
   // Simulate a frame swap from the compositor.
@@ -1620,14 +1620,14 @@
   NSWindow* parent = MakeBorderlessNativeParent();
   Widget* dialog = views::DialogDelegate::CreateDialogWidget(
       new DialogDelegateView, nullptr, [parent contentView]);
-  NativeWidgetMacNSWindowHost* bridge_host =
+  NativeWidgetMacNSWindowHost* window_host =
       NativeWidgetMacNSWindowHost::GetFromNativeWindow(
           dialog->GetNativeWindow());
 
-  EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(),
+  EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(),
             parent);
   Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]);
-  EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(),
+  EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(),
             parent);
 
   [parent close];
@@ -1636,13 +1636,13 @@
   parent = parent_widget->GetNativeWindow().GetNativeNSWindow();
   dialog = views::DialogDelegate::CreateDialogWidget(
       new DialogDelegateView, nullptr, [parent contentView]);
-  bridge_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
+  window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
       dialog->GetNativeWindow());
 
-  EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(),
+  EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(),
             parent);
   Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]);
-  EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(),
+  EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(),
             parent);
 
   parent_widget->CloseNow();
@@ -2204,7 +2204,7 @@
     widget_ = CreateTopLevelPlatformWidget();
     bridge_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow(
                   widget_->GetNativeWindow())
-                  ->bridge_impl();
+                  ->GetInProcessNSWindowBridge();
     fake_full_keyboard_access_ =
         ui::test::ScopedFakeFullKeyboardAccess::GetInstance();
     DCHECK(fake_full_keyboard_access_);
diff --git a/ui/webui/resources/polymer_resources_v3.grdp b/ui/webui/resources/polymer_resources_v3.grdp
index da855bd..db114b80 100644
--- a/ui/webui/resources/polymer_resources_v3.grdp
+++ b/ui/webui/resources/polymer_resources_v3.grdp
@@ -312,310 +312,9 @@
              file="../../../third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js"
              type="chrome_html"
              compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_CLOSURE_TYPES_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/closure-types.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_POLYMER_DOM_API_EXTERNS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-dom-api-externs.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_POLYMER_EXTERNS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-externs.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_POLYMER_ICONSET_EXTERNS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-iconset-externs.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_POLYMER_INTERNAL_SHARED_TYPES_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-shared-types.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_WEBCOMPONENTS_EXTERNS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/webcomponents-externs.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_ARRAY_SELECTOR_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/array-selector.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_CUSTOM_STYLE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/custom-style.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_DOM_BIND_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-bind.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_DOM_IF_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-if.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_DOM_MODULE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-module.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_DOM_REPEAT_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-repeat.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_CLASS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/class.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_LEGACY_DATA_MIXIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-data-mixin.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_LEGACY_ELEMENT_MIXIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-element-mixin.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_MUTABLE_DATA_BEHAVIOR_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/mutable-data-behavior.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_POLYMER_DOM_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer.dom.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_POLYMER_FN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer-fn.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_TEMPLATIZER_BEHAVIOR_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/templatizer-behavior.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_DIR_MIXIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/dir-mixin.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_DISABLE_UPGRADE_MIXIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/disable-upgrade-mixin.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_ELEMENT_MIXIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/element-mixin.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_GESTURE_EVENT_LISTENERS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/gesture-event-listeners.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_MUTABLE_DATA_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/mutable-data.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_PROPERTIES_CHANGED_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-changed.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_PROPERTIES_MIXIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-mixin.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_PROPERTY_ACCESSORS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-accessors.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_PROPERTY_EFFECTS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-effects.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_STRICT_BINDING_PARSER_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/strict-binding-parser.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_TEMPLATE_STAMP_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/template-stamp.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_ARRAY_SPLICE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/array-splice.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_ASYNC_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/async.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_BOOT_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/boot.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_CASE_MAP_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/case-map.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_DEBOUNCE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/debounce.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_FLATTENED_NODES_OBSERVER_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flattened-nodes-observer.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_FLUSH_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flush.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_GESTURES_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/gestures.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_HTML_TAG_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/html-tag.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_MIXIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/mixin.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_PATH_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/path.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_RENDER_STATUS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/render-status.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_RESOLVE_URL_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/resolve-url.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_SETTINGS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/settings.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_STYLE_GATHER_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/style-gather.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_TELEMETRY_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/telemetry.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_TEMPLATIZE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/templatize.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_UNRESOLVED_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/unresolved.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_WRAP_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/wrap.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_POLYMER_ELEMENT_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/polymer-element.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_POLYMER_POLYMER_LEGACY_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/polymer/polymer-legacy.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_APPLY_SHIM_MIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.min.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_CUSTOM_STYLE_INTERFACE_MIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.min.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_ENTRYPOINTS_APPLY_SHIM_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/apply-shim.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_ENTRYPOINTS_CUSTOM_STYLE_INTERFACE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/custom-style-interface.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_ENTRYPOINTS_SCOPING_SHIM_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/scoping-shim.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_EXTERNS_SHADYCSS_EXTERNS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/externs/shadycss-externs.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SCOPING_SHIM_MIN_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/scoping-shim.min.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_APPLY_SHIM_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_APPLY_SHIM_UTILS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim-utils.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_COMMON_REGEX_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/common-regex.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_COMMON_UTILS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/common-utils.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_CSS_PARSE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/css-parse.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_CUSTOM_STYLE_INTERFACE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/custom-style-interface.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_DOCUMENT_WAIT_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/document-wait.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_DOCUMENT_WATCHER_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/document-watcher.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_SCOPING_SHIM_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/scoping-shim.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_CACHE_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-cache.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_INFO_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-info.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_PLACEHOLDER_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-placeholder.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_PROPERTIES_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-properties.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_SETTINGS_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-settings.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_TRANSFORMER_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-transformer.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_UTIL_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-util.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_TEMPLATE_MAP_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/template-map.js"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_UNSCOPED_STYLE_HANDLER_JS"
-             file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/unscoped-style-handler.js"
+  <structure name="IDR_POLYMER_3_0_POLYMER_POLYMER_BUNDLED_MIN_JS"
+             file="../../../third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js"
              type="chrome_html"
              compress="gzip" />
-
 </grit-part>
 
diff --git a/ui/wm/core/OWNERS b/ui/wm/core/OWNERS
index 37a23e8..5c9931ca 100644
--- a/ui/wm/core/OWNERS
+++ b/ui/wm/core/OWNERS
@@ -1,2 +1 @@
-per-file shadow*=derat@chromium.org
 per-file shadow*=estade@chromium.org