diff --git a/DEPS b/DEPS
index 39a511b..653354c9 100644
--- a/DEPS
+++ b/DEPS
@@ -39,7 +39,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '67e676087cb847d40709ef639b2e2ecc7c99bbb2',
+  'skia_revision': 'f469fc0e0f04197f19c94c26801ba6888c6329dc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc
index 9f76064..1f6cb66 100644
--- a/base/message_loop/message_pump_perftest.cc
+++ b/base/message_loop/message_pump_perftest.cc
@@ -25,6 +25,11 @@
  public:
   ScheduleWorkTest() : counter_(0) {}
 
+  void SetUp() override {
+    if (base::ThreadTicks::IsSupported())
+      base::ThreadTicks::WaitUntilInitialized();
+  }
+
   void Increment(uint64_t amount) { counter_ += amount; }
 
   void Schedule(int index) {
diff --git a/base/threading/thread_perftest.cc b/base/threading/thread_perftest.cc
index e865ffa..d6fbc3e 100644
--- a/base/threading/thread_perftest.cc
+++ b/base/threading/thread_perftest.cc
@@ -44,7 +44,10 @@
   // To be implemented by each test. Subclass must uses threads_ such that
   // their cpu-time can be measured. Test must return from PingPong() _and_
   // call FinishMeasurement from any thread to complete the test.
-  virtual void Init() {}
+  virtual void Init() {
+    if (ThreadTicks::IsSupported())
+      ThreadTicks::WaitUntilInitialized();
+  }
   virtual void PingPong(int hops) = 0;
   virtual void Reset() {}
 
diff --git a/base/time/time.h b/base/time/time.h
index e0435d0..8da08ffd 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -79,6 +79,8 @@
 // For FILETIME in FromFileTime, until it moves to a new converter class.
 // See TODO(iyengar) below.
 #include <windows.h>
+
+#include "base/gtest_prod_util.h"
 #endif
 
 #include <limits>
@@ -733,16 +735,28 @@
 #if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \
     (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_ANDROID)
     return true;
+#elif defined(OS_WIN)
+    return IsSupportedWin();
 #else
     return false;
 #endif
   }
 
+  // Waits until the initialization is completed. Needs to be guarded with a
+  // call to IsSupported().
+  static void WaitUntilInitialized() {
+#if defined(OS_WIN)
+    WaitUntilInitializedWin();
+#endif
+  }
+
   // Returns thread-specific CPU-time on systems that support this feature.
   // Needs to be guarded with a call to IsSupported(). Use this timer
   // to (approximately) measure how much time the calling thread spent doing
   // actual work vs. being de-scheduled. May return bogus results if the thread
-  // migrates to another CPU between two calls.
+  // migrates to another CPU between two calls. Returns an empty ThreadTicks
+  // object until the initialization is completed. If a clock reading is
+  // absolutely needed, call WaitUntilInitialized() before this method.
   static ThreadTicks Now();
 
  private:
@@ -752,6 +766,19 @@
   // and testing.
   explicit ThreadTicks(int64 us) : TimeBase(us) {
   }
+
+#if defined(OS_WIN)
+  FRIEND_TEST_ALL_PREFIXES(TimeTicks, TSCTicksPerSecond);
+
+  // Returns the frequency of the TSC in ticks per second, or 0 if it hasn't
+  // been measured yet. Needs to be guarded with a call to IsSupported().
+  // This method is declared here rather than in the anonymous namespace to
+  // allow testing.
+  static double TSCTicksPerSecond();
+
+  static bool IsSupportedWin();
+  static void WaitUntilInitializedWin();
+#endif
 };
 
 // For logging use only.
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index 512fc37d..c8b403b 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -646,6 +646,7 @@
 #endif
 TEST(ThreadTicks, MAYBE_ThreadNow) {
   if (ThreadTicks::IsSupported()) {
+    ThreadTicks::WaitUntilInitialized();
     TimeTicks begin = TimeTicks::Now();
     ThreadTicks begin_thread = ThreadTicks::Now();
     // Make sure that ThreadNow value is non-zero.
diff --git a/base/time/time_win.cc b/base/time/time_win.cc
index 45436807..cadf4b6b 100644
--- a/base/time/time_win.cc
+++ b/base/time/time_win.cc
@@ -100,6 +100,26 @@
 base::LazyInstance<base::Lock>::Leaky g_high_res_lock =
     LAZY_INSTANCE_INITIALIZER;
 
+// Returns a pointer to the QueryThreadCycleTime() function from Windows.
+// Can't statically link to it because it is not available on XP.
+using QueryThreadCycleTimePtr = decltype(::QueryThreadCycleTime)*;
+QueryThreadCycleTimePtr GetQueryThreadCycleTimeFunction() {
+  static const QueryThreadCycleTimePtr query_thread_cycle_time_fn =
+      reinterpret_cast<QueryThreadCycleTimePtr>(::GetProcAddress(
+          ::GetModuleHandle(L"kernel32.dll"), "QueryThreadCycleTime"));
+  return query_thread_cycle_time_fn;
+}
+
+// Returns the current value of the performance counter.
+uint64 QPCNowRaw() {
+  LARGE_INTEGER perf_counter_now = {};
+  // According to the MSDN documentation for QueryPerformanceCounter(), this
+  // will never fail on systems that run XP or later.
+  // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
+  ::QueryPerformanceCounter(&perf_counter_now);
+  return perf_counter_now.QuadPart;
+}
+
 }  // namespace
 
 // Time -----------------------------------------------------------------------
@@ -415,9 +435,7 @@
 }
 
 TimeDelta QPCNow() {
-  LARGE_INTEGER now;
-  QueryPerformanceCounter(&now);
-  return QPCValueToTimeDelta(now.QuadPart);
+  return QPCValueToTimeDelta(QPCNowRaw());
 }
 
 bool IsBuggyAthlon(const base::CPU& cpu) {
@@ -504,8 +522,94 @@
 
 // static
 ThreadTicks ThreadTicks::Now() {
-  NOTREACHED();
-  return ThreadTicks();
+  DCHECK(IsSupported());
+
+  // Get the number of TSC ticks used by the current thread.
+  ULONG64 thread_cycle_time = 0;
+  GetQueryThreadCycleTimeFunction()(::GetCurrentThread(), &thread_cycle_time);
+
+  // Get the frequency of the TSC.
+  double tsc_ticks_per_second = TSCTicksPerSecond();
+  if (tsc_ticks_per_second == 0)
+    return ThreadTicks();
+
+  // Return the CPU time of the current thread.
+  double thread_time_seconds = thread_cycle_time / tsc_ticks_per_second;
+  return ThreadTicks(static_cast<int64>(
+      thread_time_seconds * Time::kMicrosecondsPerSecond));
+}
+
+// static
+bool ThreadTicks::IsSupportedWin() {
+  static bool is_supported = GetQueryThreadCycleTimeFunction() &&
+                             base::CPU().has_non_stop_time_stamp_counter() &&
+                             !IsBuggyAthlon(base::CPU());
+  return is_supported;
+}
+
+// static
+void ThreadTicks::WaitUntilInitializedWin() {
+  while (TSCTicksPerSecond() == 0)
+    ::Sleep(10);
+}
+
+double ThreadTicks::TSCTicksPerSecond() {
+  DCHECK(IsSupported());
+
+  // The value returned by QueryPerformanceFrequency() cannot be used as the TSC
+  // frequency, because there is no guarantee that the TSC frequency is equal to
+  // the performance counter frequency.
+
+  // The TSC frequency is cached in a static variable because it takes some time
+  // to compute it.
+  static double tsc_ticks_per_second = 0;
+  if (tsc_ticks_per_second != 0)
+    return tsc_ticks_per_second;
+
+  // Increase the thread priority to reduces the chances of having a context
+  // switch during a reading of the TSC and the performance counter.
+  int previous_priority = ::GetThreadPriority(::GetCurrentThread());
+  ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+
+  // The first time that this function is called, make an initial reading of the
+  // TSC and the performance counter.
+  static const uint64 tsc_initial = __rdtsc();
+  static const uint64 perf_counter_initial = QPCNowRaw();
+
+  // Make a another reading of the TSC and the performance counter every time
+  // that this function is called.
+  uint64 tsc_now = __rdtsc();
+  uint64 perf_counter_now = QPCNowRaw();
+
+  // Reset the thread priority.
+  ::SetThreadPriority(::GetCurrentThread(), previous_priority);
+
+  // Make sure that at least 50 ms elapsed between the 2 readings. The first
+  // time that this function is called, we don't expect this to be the case.
+  // Note: The longer the elapsed time between the 2 readings is, the more
+  //   accurate the computed TSC frequency will be. The 50 ms value was
+  //   chosen because local benchmarks show that it allows us to get a
+  //   stddev of less than 1 tick/us between multiple runs.
+  // Note: According to the MSDN documentation for QueryPerformanceFrequency(),
+  //   this will never fail on systems that run XP or later.
+  //   https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx
+  LARGE_INTEGER perf_counter_frequency = {};
+  ::QueryPerformanceFrequency(&perf_counter_frequency);
+  DCHECK_GE(perf_counter_now, perf_counter_initial);
+  uint64 perf_counter_ticks = perf_counter_now - perf_counter_initial;
+  double elapsed_time_seconds =
+      perf_counter_ticks / static_cast<double>(perf_counter_frequency.QuadPart);
+
+  const double kMinimumEvaluationPeriodSeconds = 0.05;
+  if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
+    return 0;
+
+  // Compute the frequency of the TSC.
+  DCHECK_GE(tsc_now, tsc_initial);
+  uint64 tsc_ticks = tsc_now - tsc_initial;
+  tsc_ticks_per_second = tsc_ticks / elapsed_time_seconds;
+
+  return tsc_ticks_per_second;
 }
 
 // static
diff --git a/base/time/time_win_unittest.cc b/base/time/time_win_unittest.cc
index c894b68..6f8a9b7d 100644
--- a/base/time/time_win_unittest.cc
+++ b/base/time/time_win_unittest.cc
@@ -12,13 +12,10 @@
 
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
+#include "base/win/registry.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::Time;
-using base::TimeDelta;
-using base::TimeTicks;
-using base::TraceTicks;
-
+namespace base {
 namespace {
 
 class MockTimeTicks : public TimeTicks {
@@ -192,18 +189,22 @@
   // in order to create a single test case list.
   COMPILE_ASSERT(sizeof(TimeTicks) == sizeof(Time),
                  test_only_works_with_same_sizes);
-  TestCase cases[] = {
-    { reinterpret_cast<TestFunc>(&Time::Now), "Time::Now" },
-    { &TimeTicks::Now, "TimeTicks::Now" },
-    { reinterpret_cast<TestFunc>(&TraceTicks::Now), "TraceTicks::Now" },
-    { NULL, "" }
-  };
+  std::vector<TestCase> cases;
+  cases.push_back({reinterpret_cast<TestFunc>(&Time::Now), "Time::Now"});
+  cases.push_back({&TimeTicks::Now, "TimeTicks::Now"});
+  cases.push_back(
+      {reinterpret_cast<TestFunc>(&TraceTicks::Now), "TraceTicks::Now"});
 
-  int test_case = 0;
-  while (cases[test_case].func) {
+  if (ThreadTicks::IsSupported()) {
+    ThreadTicks::WaitUntilInitialized();
+    cases.push_back(
+        {reinterpret_cast<TestFunc>(&ThreadTicks::Now), "ThreadTicks::Now"});
+  }
+
+  for (const auto& test_case : cases) {
     TimeTicks start = TimeTicks::Now();
     for (int index = 0; index < kLoops; index++)
-      cases[test_case].func();
+      test_case.func();
     TimeTicks stop = TimeTicks::Now();
     // Turning off the check for acceptible delays.  Without this check,
     // the test really doesn't do much other than measure.  But the
@@ -213,9 +214,29 @@
     // slow, and there is really no value for checking against a max timer.
     //const int kMaxTime = 35;  // Maximum acceptible milliseconds for test.
     //EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
-    printf("%s: %1.2fus per call\n", cases[test_case].description,
-      (stop - start).InMillisecondsF() * 1000 / kLoops);
-    test_case++;
+    printf("%s: %1.2fus per call\n", test_case.description,
+           (stop - start).InMillisecondsF() * 1000 / kLoops);
+  }
+}
+
+TEST(TimeTicks, TSCTicksPerSecond) {
+  if (ThreadTicks::IsSupported()) {
+    ThreadTicks::WaitUntilInitialized();
+
+    // Read the CPU frequency from the registry.
+    base::win::RegKey processor_key(
+        HKEY_LOCAL_MACHINE,
+        L"Hardware\\Description\\System\\CentralProcessor\\0", KEY_QUERY_VALUE);
+    ASSERT_TRUE(processor_key.Valid());
+    DWORD processor_mhz_from_registry;
+    ASSERT_EQ(ERROR_SUCCESS,
+              processor_key.ReadValueDW(L"~MHz", &processor_mhz_from_registry));
+
+    // Expect the measured TSC frequency to be similar to the processor
+    // frequency from the registry (0.5% error).
+    double tsc_mhz_measured = ThreadTicks::TSCTicksPerSecond() / 1e6;
+    EXPECT_NEAR(tsc_mhz_measured, processor_mhz_from_registry,
+                0.005 * processor_mhz_from_registry);
   }
 }
 
@@ -273,3 +294,5 @@
         << (ticks < Time::kQPCOverflowThreshold ? "FAST" : "SAFE");
   }
 }
+
+}  // namespace base
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
index e78ee61..66f231c 100644
--- a/base/trace_event/trace_event_impl.cc
+++ b/base/trace_event/trace_event_impl.cc
@@ -177,7 +177,11 @@
                                 const ThreadTicks& thread_now) {
   DCHECK_EQ(duration_.ToInternalValue(), -1);
   duration_ = now - timestamp_;
-  thread_duration_ = thread_now - thread_timestamp_;
+
+  // |thread_timestamp_| can be empty if the thread ticks clock wasn't
+  // initialized when it was recorded.
+  if (thread_timestamp_ != ThreadTicks())
+    thread_duration_ = thread_now - thread_timestamp_;
 }
 
 void TraceEvent::EstimateTraceMemoryOverhead(
diff --git a/cc/debug/lap_timer.cc b/cc/debug/lap_timer.cc
index eb6900c..2c5d381 100644
--- a/cc/debug/lap_timer.cc
+++ b/cc/debug/lap_timer.cc
@@ -42,6 +42,8 @@
     : LapTimer(kWarmupRuns,
                base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
                kTimeCheckInterval) {
+  if (base::ThreadTicks::IsSupported())
+    base::ThreadTicks::WaitUntilInitialized();
 }
 
 void LapTimer::Reset() {
diff --git a/cc/debug/rendering_stats_instrumentation.cc b/cc/debug/rendering_stats_instrumentation.cc
index e793c5022..d80d912e 100644
--- a/cc/debug/rendering_stats_instrumentation.cc
+++ b/cc/debug/rendering_stats_instrumentation.cc
@@ -14,6 +14,8 @@
 
 RenderingStatsInstrumentation::RenderingStatsInstrumentation()
     : record_rendering_stats_(false) {
+  if (base::ThreadTicks::IsSupported())
+    base::ThreadTicks::WaitUntilInitialized();
 }
 
 RenderingStatsInstrumentation::~RenderingStatsInstrumentation() {}
diff --git a/chrome/VERSION b/chrome/VERSION
index ef5f09e..1110489e 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=48
 MINOR=0
-BUILD=2536
+BUILD=2537
 PATCH=0
diff --git a/chrome/browser/android/preferences/pref_service_bridge.cc b/chrome/browser/android/preferences/pref_service_bridge.cc
index c2b2c58..5b5f4b5 100644
--- a/chrome/browser/android/preferences/pref_service_bridge.cc
+++ b/chrome/browser/android/preferences/pref_service_bridge.cc
@@ -70,7 +70,6 @@
     case CONTENT_SETTING_BLOCK:
       return false;
     case CONTENT_SETTING_ALLOW:
-      return true;
     case CONTENT_SETTING_ASK:
     default:
       return true;
diff --git a/chrome/browser/download/notification/download_item_notification.cc b/chrome/browser/download/notification/download_item_notification.cc
index 41c1d8d7..3c05228f 100644
--- a/chrome/browser/download/notification/download_item_notification.cc
+++ b/chrome/browser/download/notification/download_item_notification.cc
@@ -204,6 +204,7 @@
   if (item_ && item_->IsDangerous() && !item_->IsDone()) {
     content::RecordAction(
         UserMetricsAction("DownloadNotification.Close_Dangerous"));
+    closed_ = true;  // Should be set before cancelling the download.
     item_->Cancel(true /* by_user */);
     return;
   }
@@ -331,7 +332,7 @@
        (download_state == content::DownloadItem::INTERRUPTED &&
         previous_download_state_ != content::DownloadItem::INTERRUPTED));
 
-  if (IsNotificationVisible()) {
+  if (IsNotificationVisible() && !closed_) {
     UpdateNotificationData(popup ? UPDATE_AND_POPUP : UPDATE);
   } else {
     if (show_next_ || popup)
@@ -416,6 +417,7 @@
   notification_->set_buttons(notification_actions);
 
   if (type == ADD) {
+    closed_ = false;
     g_browser_process->notification_ui_manager()->
         Add(*notification_, profile());
   } else if (type == UPDATE ||
@@ -443,6 +445,7 @@
         Update(*notification_, profile());
   } else if (type == UPDATE_AND_POPUP) {
     CloseNotificationByNonUser();
+    closed_ = false;
     g_browser_process->notification_ui_manager()->
         Add(*notification_, profile());
   } else {
@@ -574,6 +577,7 @@
   // doesn't pop-up itself so this logic works as disabling pop-up.
   CloseNotificationByNonUser();
   notification_->set_priority(message_center::LOW_PRIORITY);
+  closed_ = false;
   g_browser_process->notification_ui_manager()->Add(*notification_, profile());
 }
 
diff --git a/chrome/browser/download/notification/download_item_notification.h b/chrome/browser/download/notification/download_item_notification.h
index 0506235..50f87ef 100644
--- a/chrome/browser/download/notification/download_item_notification.h
+++ b/chrome/browser/download/notification/download_item_notification.h
@@ -106,6 +106,10 @@
   // goes visible. The initial value is true so it gets shown on initial update.
   bool show_next_ = true;
 
+  // Flag if the notification has been closed or not. Setting this flag
+  // prevents updates after close.
+  bool closed_ = false;
+
   int image_resource_id_ = 0;
   std::pair<gfx::VectorIconId, SkColor> vector_icon_params_;
   content::DownloadItem::DownloadState previous_download_state_ =
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index ea31298..d830cfa 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1407,14 +1407,8 @@
 #endif
 
     case IDC_SPELLCHECK_MENU:
-      return true;
-
     case IDC_CONTENT_CONTEXT_OPENLINKWITH:
-      return true;
-
     case IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_SETTINGS:
-      return true;
-
     case IDC_CONTENT_CONTEXT_FORCESAVEPASSWORD:
       return true;
 
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
index 645b600..3edcc691 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
@@ -228,9 +228,6 @@
       return succeeded_;
 
     case IDC_CONTENT_CONTEXT_SPELLING_TOGGLE:
-      return integrate_spelling_service_.IsUserModifiable() &&
-             !profile->IsOffTheRecord();
-
     case IDC_CONTENT_CONTEXT_AUTOCORRECT_SPELLING_TOGGLE:
       return integrate_spelling_service_.IsUserModifiable() &&
              !profile->IsOffTheRecord();
diff --git a/chrome/browser/resources/md_downloads/compiled_resources.gyp b/chrome/browser/resources/md_downloads/compiled_resources.gyp
index 33835847..a5a4e65 100644
--- a/chrome/browser/resources/md_downloads/compiled_resources.gyp
+++ b/chrome/browser/resources/md_downloads/compiled_resources.gyp
@@ -18,6 +18,10 @@
           '../../../../ui/webui/resources/js/cr/ui/focus_row.js',
           '../../../../ui/webui/resources/js/cr/ui/focus_grid.js',
           '../../../../ui/webui/resources/js/util.js',
+          '../../../../third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior-extracted.js',
+          '../../../../third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js',
+          '../../../../third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior-extracted.js',
+          '../../../../third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js',
           'action_service.js',
           'constants.js',
           'throttled_icon_loader.js',
diff --git a/chrome/browser/resources/md_downloads/crisper.js b/chrome/browser/resources/md_downloads/crisper.js
index d0a64e3..de97497 100644
--- a/chrome/browser/resources/md_downloads/crisper.js
+++ b/chrome/browser/resources/md_downloads/crisper.js
@@ -2353,67 +2353,6 @@
 
   extends: 'a',
 });
-// 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.
-
-/** @typedef {{img: HTMLImageElement, url: string}} */
-var LoadIconRequest;
-
-cr.define('downloads', function() {
-  /**
-   * @param {number} maxAllowed The maximum number of simultaneous downloads
-   *     allowed.
-   * @constructor
-   */
-  function ThrottledIconLoader(maxAllowed) {
-    assert(maxAllowed > 0);
-
-    /** @private {number} */
-    this.maxAllowed_ = maxAllowed;
-
-    /** @private {!Array<!LoadIconRequest>} */
-    this.requests_ = [];
-  }
-
-  ThrottledIconLoader.prototype = {
-    /** @private {number} */
-    loading_: 0,
-
-    /**
-     * Load the provided |url| into |img.src| after appending ?scale=.
-     * @param {!HTMLImageElement} img An <img> to show the loaded image in.
-     * @param {string} url A remote image URL to load.
-     */
-    loadScaledIcon: function(img, url) {
-      var scaledUrl = url + '?scale=' + window.devicePixelRatio + 'x';
-      if (img.src == scaledUrl)
-        return;
-
-      this.requests_.push({img: img, url: scaledUrl});
-      this.loadNextIcon_();
-    },
-
-    /** @private */
-    loadNextIcon_: function() {
-      if (this.loading_ > this.maxAllowed_ || !this.requests_.length)
-        return;
-
-      var request = this.requests_.shift();
-      var img = request.img;
-
-      img.onabort = img.onerror = img.onload = function() {
-        this.loading_--;
-        this.loadNextIcon_();
-      }.bind(this);
-
-      this.loading_++;
-      img.src = request.url;
-    },
-  };
-
-  return {ThrottledIconLoader: ThrottledIconLoader};
-});
 // Copyright 2014 Google Inc. All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -3007,7 +2946,7 @@
 }
 }
 });
-Polymer.version = '1.1.4';
+Polymer.version = '1.1.5';
 Polymer.Base._addFeature({
 _registerFeatures: function () {
 this._prepIs();
@@ -4884,7 +4823,15 @@
 }
 },
 listen: function (node, eventName, methodName) {
-this._listen(node, eventName, this._createEventHandler(node, eventName, methodName));
+var handler = this._recallEventHandler(this, eventName, node, methodName);
+if (!handler) {
+handler = this._createEventHandler(node, eventName, methodName);
+}
+if (handler._listening) {
+return;
+}
+this._listen(node, eventName, handler);
+handler._listening = true;
 },
 _boundListenerKey: function (eventName, methodName) {
 return eventName + ':' + methodName;
@@ -4923,6 +4870,7 @@
 host._warn(host._logf('_createEventHandler', 'listener method `' + methodName + '` not defined'));
 }
 };
+handler._listening = false;
 this._recordEventHandler(host, eventName, node, methodName, handler);
 return handler;
 },
@@ -4930,6 +4878,7 @@
 var handler = this._recallEventHandler(this, eventName, node, methodName);
 if (handler) {
 this._unlisten(node, eventName, handler);
+handler._listening = false;
 }
 },
 _listen: function (node, eventName, handler) {
@@ -5777,6 +5726,12 @@
 }
 }
 return elt;
+},
+isLightDescendant: function (node) {
+return this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(node).getOwnerRoot();
+},
+isLocalDescendant: function (node) {
+return this.root === Polymer.dom(node).getOwnerRoot();
 }
 });
 Polymer.Bind = {
@@ -6578,6 +6533,22 @@
 this._notifySplice(array, path, 0, args.length, []);
 }
 return ret;
+},
+prepareModelNotifyPath: function (model) {
+this.mixin(model, {
+fire: Polymer.Base.fire,
+notifyPath: Polymer.Base.notifyPath,
+_EVENT_CHANGED: Polymer.Base._EVENT_CHANGED,
+_notifyPath: Polymer.Base._notifyPath,
+_pathEffector: Polymer.Base._pathEffector,
+_annotationPathEffect: Polymer.Base._annotationPathEffect,
+_complexObserverPathEffect: Polymer.Base._complexObserverPathEffect,
+_annotatedComputationPathEffect: Polymer.Base._annotatedComputationPathEffect,
+_computePathEffect: Polymer.Base._computePathEffect,
+_modelForPath: Polymer.Base._modelForPath,
+_pathMatchesEffect: Polymer.Base._pathMatchesEffect,
+_notifyBoundPaths: Polymer.Base._notifyBoundPaths
+});
 }
 });
 }());
@@ -7784,6 +7755,7 @@
 _instanceProps: Polymer.nob,
 _parentPropPrefix: '_parent_',
 templatize: function (template) {
+this._templatized = template;
 if (!template._content) {
 template._content = template.content;
 }
@@ -7794,11 +7766,11 @@
 }
 var archetype = Object.create(Polymer.Base);
 this._customPrepAnnotations(archetype, template);
+this._prepParentProperties(archetype, template);
 archetype._prepEffects();
 this._customPrepEffects(archetype);
 archetype._prepBehaviors();
 archetype._prepBindings();
-this._prepParentProperties(archetype, template);
 archetype._notifyPath = this._notifyPathImpl;
 archetype._scopeElementClass = this._scopeElementClassImpl;
 archetype.listen = this._listenImpl;
@@ -7881,6 +7853,7 @@
 proto = archetype._parentPropProto = Object.create(null);
 if (template != this) {
 Polymer.Bind.prepareModel(proto);
+Polymer.Base.prepareModelNotifyPath(proto);
 }
 for (prop in parentProps) {
 var parentProp = this._parentPropPrefix + prop;
@@ -7899,6 +7872,7 @@
 template._forwardParentProp = this._forwardParentProp.bind(this);
 }
 this._extendTemplate(template, proto);
+template._pathEffector = this._pathEffectorImpl.bind(this);
 }
 },
 _createForwardPropEffector: function (prop) {
@@ -7909,7 +7883,7 @@
 _createHostPropEffector: function (prop) {
 var prefix = this._parentPropPrefix;
 return function (source, value) {
-this.dataHost[prefix + prop] = value;
+this.dataHost._templatized[prefix + prop] = value;
 };
 },
 _createInstancePropEffector: function (prop) {
@@ -7941,16 +7915,17 @@
 var root = dot < 0 ? path : path.slice(0, dot);
 dataHost._forwardInstancePath.call(dataHost, this, path, value);
 if (root in dataHost._parentProps) {
-dataHost.notifyPath(dataHost._parentPropPrefix + path, value);
+dataHost._templatized.notifyPath(dataHost._parentPropPrefix + path, value);
 }
 },
-_pathEffector: function (path, value, fromAbove) {
+_pathEffectorImpl: function (path, value, fromAbove) {
 if (this._forwardParentPath) {
 if (path.indexOf(this._parentPropPrefix) === 0) {
-this._forwardParentPath(path.substring(8), value);
+var subPath = path.substring(this._parentPropPrefix.length);
+this._forwardParentPath(subPath, value);
 }
 }
-Polymer.Base._pathEffector.apply(this, arguments);
+Polymer.Base._pathEffector.call(this._templatized, path, value, fromAbove);
 },
 _constructorImpl: function (model, host) {
 this._rootDataHost = host._getRootDataHost();
@@ -7993,8 +7968,9 @@
 stamp: function (model) {
 model = model || {};
 if (this._parentProps) {
+var templatized = this._templatized;
 for (var prop in this._parentProps) {
-model[prop] = this[this._parentPropPrefix + prop];
+model[prop] = templatized[this._parentPropPrefix + prop];
 }
 }
 return new this.ctor(model, this);
@@ -8602,7 +8578,7 @@
 }
 } else {
 this.push('selected', item);
-skey = this._selectedColl.getKey(item);
+var skey = this._selectedColl.getKey(item);
 this.linkPaths('selected.' + skey, 'items.' + key);
 }
 } else {
@@ -8771,6 +8747,1336 @@
 this.fire('dom-change');
 }
 });
+/**
+   * `IronResizableBehavior` is a behavior that can be used in Polymer elements to
+   * coordinate the flow of resize events between "resizers" (elements that control the
+   * size or hidden state of their children) and "resizables" (elements that need to be
+   * notified when they are resized or un-hidden by their parents in order to take
+   * action on their new measurements).
+   * Elements that perform measurement should add the `IronResizableBehavior` behavior to
+   * their element definition and listen for the `iron-resize` event on themselves.
+   * This event will be fired when they become showing after having been hidden,
+   * when they are resized explicitly by another resizable, or when the window has been
+   * resized.
+   * Note, the `iron-resize` event is non-bubbling.
+   *
+   * @polymerBehavior Polymer.IronResizableBehavior
+   * @demo demo/index.html
+   **/
+  Polymer.IronResizableBehavior = {
+    properties: {
+      /**
+       * The closest ancestor element that implements `IronResizableBehavior`.
+       */
+      _parentResizable: {
+        type: Object,
+        observer: '_parentResizableChanged'
+      },
+
+      /**
+       * True if this element is currently notifying its descedant elements of
+       * resize.
+       */
+      _notifyingDescendant: {
+        type: Boolean,
+        value: false
+      }
+    },
+
+    listeners: {
+      'iron-request-resize-notifications': '_onIronRequestResizeNotifications'
+    },
+
+    created: function() {
+      // We don't really need property effects on these, and also we want them
+      // to be created before the `_parentResizable` observer fires:
+      this._interestedResizables = [];
+      this._boundNotifyResize = this.notifyResize.bind(this);
+    },
+
+    attached: function() {
+      this.fire('iron-request-resize-notifications', null, {
+        node: this,
+        bubbles: true,
+        cancelable: true
+      });
+
+      if (!this._parentResizable) {
+        window.addEventListener('resize', this._boundNotifyResize);
+        this.notifyResize();
+      }
+    },
+
+    detached: function() {
+      if (this._parentResizable) {
+        this._parentResizable.stopResizeNotificationsFor(this);
+      } else {
+        window.removeEventListener('resize', this._boundNotifyResize);
+      }
+
+      this._parentResizable = null;
+    },
+
+    /**
+     * Can be called to manually notify a resizable and its descendant
+     * resizables of a resize change.
+     */
+    notifyResize: function() {
+      if (!this.isAttached) {
+        return;
+      }
+
+      this._interestedResizables.forEach(function(resizable) {
+        if (this.resizerShouldNotify(resizable)) {
+          this._notifyDescendant(resizable);
+        }
+      }, this);
+
+      this._fireResize();
+    },
+
+    /**
+     * Used to assign the closest resizable ancestor to this resizable
+     * if the ancestor detects a request for notifications.
+     */
+    assignParentResizable: function(parentResizable) {
+      this._parentResizable = parentResizable;
+    },
+
+    /**
+     * Used to remove a resizable descendant from the list of descendants
+     * that should be notified of a resize change.
+     */
+    stopResizeNotificationsFor: function(target) {
+      var index = this._interestedResizables.indexOf(target);
+
+      if (index > -1) {
+        this._interestedResizables.splice(index, 1);
+        this.unlisten(target, 'iron-resize', '_onDescendantIronResize');
+      }
+    },
+
+    /**
+     * This method can be overridden to filter nested elements that should or
+     * should not be notified by the current element. Return true if an element
+     * should be notified, or false if it should not be notified.
+     *
+     * @param {HTMLElement} element A candidate descendant element that
+     * implements `IronResizableBehavior`.
+     * @return {boolean} True if the `element` should be notified of resize.
+     */
+    resizerShouldNotify: function(element) { return true; },
+
+    _onDescendantIronResize: function(event) {
+      if (this._notifyingDescendant) {
+        event.stopPropagation();
+        return;
+      }
+
+      // NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the
+      // otherwise non-bubbling event "just work." We do it manually here for
+      // the case where Polymer is not using shadow roots for whatever reason:
+      if (!Polymer.Settings.useShadow) {
+        this._fireResize();
+      }
+    },
+
+    _fireResize: function() {
+      this.fire('iron-resize', null, {
+        node: this,
+        bubbles: false
+      });
+    },
+
+    _onIronRequestResizeNotifications: function(event) {
+      var target = event.path ? event.path[0] : event.target;
+
+      if (target === this) {
+        return;
+      }
+
+      if (this._interestedResizables.indexOf(target) === -1) {
+        this._interestedResizables.push(target);
+        this.listen(target, 'iron-resize', '_onDescendantIronResize');
+      }
+
+      target.assignParentResizable(this);
+      this._notifyDescendant(target);
+
+      event.stopPropagation();
+    },
+
+    _parentResizableChanged: function(parentResizable) {
+      if (parentResizable) {
+        window.removeEventListener('resize', this._boundNotifyResize);
+      }
+    },
+
+    _notifyDescendant: function(descendant) {
+      // NOTE(cdata): In IE10, attached is fired on children first, so it's
+      // important not to notify them if the parent is not attached yet (or
+      // else they will get redundantly notified when the parent attaches).
+      if (!this.isAttached) {
+        return;
+      }
+
+      this._notifyingDescendant = true;
+      descendant.notifyResize();
+      this._notifyingDescendant = false;
+    }
+  };
+(function() {
+
+  var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/);
+  var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
+  var DEFAULT_PHYSICAL_COUNT = 20;
+  var MAX_PHYSICAL_COUNT = 500;
+
+  Polymer({
+
+    is: 'iron-list',
+
+    properties: {
+
+      /**
+       * 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
+       * for the row.  If `sort` is provided, the index will reflect the
+       * sorted order (rather than the original array order).
+       */
+      indexAs: {
+        type: String,
+        value: 'index'
+      },
+
+      /**
+       * The name of the variable to add to the binding scope to indicate
+       * if the row is selected.
+       */
+      selectedAs: {
+        type: String,
+        value: 'selected'
+      },
+
+      /**
+       * When true, tapping a row will select the item, placing its data model
+       * in the set of selected items retrievable via the selection property.
+       *
+       * Note that tapping focusable elements within the list item will not
+       * result in selection, since they are presumed to have their * own action.
+       */
+      selectionEnabled: {
+        type: Boolean,
+        value: false
+      },
+
+      /**
+       * When `multiSelection` is false, this is the currently selected item, or `null`
+       * if no item is selected.
+       */
+      selectedItem: {
+        type: Object,
+        notify: true
+      },
+
+      /**
+       * When `multiSelection` is true, this is an array that contains the selected items.
+       */
+      selectedItems: {
+        type: Object,
+        notify: true
+      },
+
+      /**
+       * 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.
+       */
+      multiSelection: {
+        type: Boolean,
+        value: false
+      }
+    },
+
+    observers: [
+      '_itemsChanged(items.*)',
+      '_selectionEnabledChanged(selectionEnabled)',
+      '_multiSelectionChanged(multiSelection)'
+    ],
+
+    behaviors: [
+      Polymer.Templatizer,
+      Polymer.IronResizableBehavior
+    ],
+
+    listeners: {
+      'iron-resize': '_resizeHandler'
+    },
+
+    /**
+     * The ratio of hidden tiles that should remain in the scroll direction.
+     * Recommended value ~0.5, so it will distribute tiles evely in both directions.
+     */
+    _ratio: 0.5,
+
+    /**
+     * The element that controls the scroll
+     * @type {?Element}
+     */
+    _scroller: null,
+
+    /**
+     * The padding-top value of the `scroller` element
+     */
+    _scrollerPaddingTop: 0,
+
+    /**
+     * This value is the same as `scrollTop`.
+     */
+    _scrollPosition: 0,
+
+    /**
+     * The number of tiles in the DOM.
+     */
+    _physicalCount: 0,
+
+    /**
+     * The k-th tile that is at the top of the scrolling list.
+     */
+    _physicalStart: 0,
+
+    /**
+     * The k-th tile that is at the bottom of the scrolling list.
+     */
+    _physicalEnd: 0,
+
+    /**
+     * The sum of the heights of all the tiles in the DOM.
+     */
+    _physicalSize: 0,
+
+    /**
+     * The average `offsetHeight` of the tiles observed till now.
+     */
+    _physicalAverage: 0,
+
+    /**
+     * The number of tiles which `offsetHeight` > 0 observed until now.
+     */
+    _physicalAverageCount: 0,
+
+    /**
+     * The Y position of the item rendered in the `_physicalStart`
+     * tile relative to the scrolling list.
+     */
+    _physicalTop: 0,
+
+    /**
+     * The number of items in the list.
+     */
+    _virtualCount: 0,
+
+    /**
+     * The n-th item rendered in the `_physicalStart` tile.
+     */
+    _virtualStartVal: 0,
+
+    /**
+     * A map between an item key and its physical item index
+     */
+    _physicalIndexForKey: null,
+
+    /**
+     * The estimated scroll height based on `_physicalAverage`
+     */
+    _estScrollHeight: 0,
+
+    /**
+     * The scroll height of the dom node
+     */
+    _scrollHeight: 0,
+
+    /**
+     * The size of the viewport
+     */
+    _viewportSize: 0,
+
+    /**
+     * An array of DOM nodes that are currently in the tree
+     * @type {?Array<!TemplatizerNode>}
+     */
+    _physicalItems: null,
+
+    /**
+     * An array of heights for each item in `_physicalItems`
+     * @type {?Array<number>}
+     */
+    _physicalSizes: null,
+
+    /**
+     * A cached value for the visible index.
+     * See `firstVisibleIndex`
+     * @type {?number}
+     */
+    _firstVisibleIndexVal: null,
+
+    /**
+     * A Polymer collection for the items.
+     * @type {?Polymer.Collection}
+     */
+    _collection: null,
+
+    /**
+     * True if the current item list was rendered for the first time
+     * after attached.
+     */
+    _itemsRendered: false,
+
+    /**
+     * The bottom of the physical content.
+     */
+    get _physicalBottom() {
+      return this._physicalTop + this._physicalSize;
+    },
+
+    /**
+     * The n-th item rendered in the last physical item.
+     */
+    get _virtualEnd() {
+      return this._virtualStartVal + this._physicalCount - 1;
+    },
+
+    /**
+     * The lowest n-th value for an item such that it can be rendered in `_physicalStart`.
+     */
+    _minVirtualStart: 0,
+
+    /**
+     * The largest n-th value for an item such that it can be rendered in `_physicalStart`.
+     */
+    get _maxVirtualStart() {
+      return this._virtualCount < this._physicalCount ?
+          this._virtualCount : this._virtualCount - this._physicalCount;
+    },
+
+    /**
+     * The height of the physical content that isn't on the screen.
+     */
+    get _hiddenContentSize() {
+      return this._physicalSize - this._viewportSize;
+    },
+
+    /**
+     * The maximum scroll top value.
+     */
+    get _maxScrollTop() {
+      return this._estScrollHeight - this._viewportSize;
+    },
+
+    /**
+     * Sets the n-th item rendered in `_physicalStart`
+     */
+    set _virtualStart(val) {
+      // clamp the value so that _minVirtualStart <= val <= _maxVirtualStart
+      this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val));
+      this._physicalStart = this._virtualStartVal % this._physicalCount;
+      this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount;
+    },
+
+    /**
+     * Gets the n-th item rendered in `_physicalStart`
+     */
+    get _virtualStart() {
+      return this._virtualStartVal;
+    },
+
+    /**
+     * An optimal physical size such that we will have enough physical items
+     * to fill up the viewport and recycle when the user scrolls.
+     *
+     * This default value assumes that we will at least have the equivalent
+     * to a viewport of physical items above and below the user's viewport.
+     */
+    get _optPhysicalSize() {
+      return this._viewportSize * 3;
+    },
+
+   /**
+    * True if the current list is visible.
+    */
+    get _isVisible() {
+      return this._scroller && Boolean(this._scroller.offsetWidth || this._scroller.offsetHeight);
+    },
+
+    /**
+     * Gets the first visible item in the viewport.
+     *
+     * @type {number}
+     */
+    get firstVisibleIndex() {
+      var physicalOffset;
+
+      if (this._firstVisibleIndexVal === null) {
+        physicalOffset = this._physicalTop;
+
+        this._firstVisibleIndexVal = this._iterateItems(
+          function(pidx, vidx) {
+            physicalOffset += this._physicalSizes[pidx];
+
+            if (physicalOffset > this._scrollPosition) {
+              return vidx;
+            }
+          }) || 0;
+      }
+
+      return this._firstVisibleIndexVal;
+    },
+
+    ready: function() {
+      if (IOS_TOUCH_SCROLLING) {
+        this._scrollListener = function() {
+          requestAnimationFrame(this._scrollHandler.bind(this));
+        }.bind(this);
+      } else {
+        this._scrollListener = this._scrollHandler.bind(this);
+      }
+    },
+
+    /**
+     * When the element has been attached to the DOM tree.
+     */
+    attached: function() {
+      // delegate to the parent's scroller
+      // e.g. paper-scroll-header-panel
+      var el = Polymer.dom(this);
+
+      var parentNode = /** @type {?{scroller: ?Element}} */ (el.parentNode);
+      if (parentNode && parentNode.scroller) {
+        this._scroller = parentNode.scroller;
+      } else {
+        this._scroller = this;
+        this.classList.add('has-scroller');
+      }
+
+      if (IOS_TOUCH_SCROLLING) {
+        this._scroller.style.webkitOverflowScrolling = 'touch';
+      }
+
+      this._scroller.addEventListener('scroll', this._scrollListener);
+
+      this.updateViewportBoundaries();
+      this._render();
+    },
+
+    /**
+     * When the element has been removed from the DOM tree.
+     */
+    detached: function() {
+      this._itemsRendered = false;
+      if (this._scroller) {
+        this._scroller.removeEventListener('scroll', this._scrollListener);
+      }
+    },
+
+    /**
+     * Invoke this method if you dynamically update the viewport's
+     * size or CSS padding.
+     *
+     * @method updateViewportBoundaries
+     */
+    updateViewportBoundaries: function() {
+      var scrollerStyle = window.getComputedStyle(this._scroller);
+      this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10);
+      this._viewportSize = this._scroller.offsetHeight;
+    },
+
+    /**
+     * Update the models, the position of the
+     * items in the viewport and recycle tiles as needed.
+     */
+    _refresh: function() {
+      var SCROLL_DIRECTION_UP = -1;
+      var SCROLL_DIRECTION_DOWN = 1;
+      var SCROLL_DIRECTION_NONE = 0;
+
+      // clamp the `scrollTop` value
+      // IE 10|11 scrollTop may go above `_maxScrollTop`
+      // iOS `scrollTop` may go below 0 and above `_maxScrollTop`
+      var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.scrollTop));
+
+      var tileHeight, kth, recycledTileSet;
+      var ratio = this._ratio;
+      var delta = scrollTop - this._scrollPosition;
+      var direction = SCROLL_DIRECTION_NONE;
+      var recycledTiles = 0;
+      var hiddenContentSize = this._hiddenContentSize;
+      var currentRatio = ratio;
+      var movingUp = [];
+
+      // track the last `scrollTop`
+      this._scrollPosition = scrollTop;
+
+      // clear cached visible index
+      this._firstVisibleIndexVal = null;
+
+      // random access
+      if (Math.abs(delta) > this._physicalSize) {
+        this._physicalTop += delta;
+        direction = SCROLL_DIRECTION_NONE;
+        recycledTiles =  Math.round(delta / this._physicalAverage);
+      }
+      // scroll up
+      else if (delta < 0) {
+        var topSpace = scrollTop - this._physicalTop;
+        var virtualStart = this._virtualStart;
+
+        direction = SCROLL_DIRECTION_UP;
+        recycledTileSet = [];
+
+        kth = this._physicalEnd;
+        currentRatio = topSpace / hiddenContentSize;
+
+        // move tiles from bottom to top
+        while (
+            // approximate `currentRatio` to `ratio`
+            currentRatio < ratio &&
+            // recycle less physical items than the total
+            recycledTiles < this._physicalCount &&
+            // ensure that these recycled tiles are needed
+            virtualStart - recycledTiles > 0
+        ) {
+
+          tileHeight = this._physicalSizes[kth] || this._physicalAverage;
+          currentRatio += tileHeight / hiddenContentSize;
+
+          recycledTileSet.push(kth);
+          recycledTiles++;
+          kth = (kth === 0) ? this._physicalCount - 1 : kth - 1;
+        }
+
+        movingUp = recycledTileSet;
+        recycledTiles = -recycledTiles;
+
+      }
+      // scroll down
+      else if (delta > 0) {
+        var bottomSpace = this._physicalBottom - (scrollTop + this._viewportSize);
+        var virtualEnd = this._virtualEnd;
+        var lastVirtualItemIndex = this._virtualCount-1;
+
+        direction = SCROLL_DIRECTION_DOWN;
+        recycledTileSet = [];
+
+        kth = this._physicalStart;
+        currentRatio = bottomSpace / hiddenContentSize;
+
+        // move tiles from top to bottom
+        while (
+            // approximate `currentRatio` to `ratio`
+            currentRatio < ratio &&
+            // recycle less physical items than the total
+            recycledTiles < this._physicalCount &&
+            // ensure that these recycled tiles are needed
+            virtualEnd + recycledTiles < lastVirtualItemIndex
+          ) {
+
+          tileHeight = this._physicalSizes[kth] || this._physicalAverage;
+          currentRatio += tileHeight / hiddenContentSize;
+
+          this._physicalTop += tileHeight;
+          recycledTileSet.push(kth);
+          recycledTiles++;
+          kth = (kth + 1) % this._physicalCount;
+        }
+      }
+
+      if (recycledTiles !== 0) {
+        this._virtualStart = this._virtualStart + recycledTiles;
+        this._update(recycledTileSet, movingUp);
+      }
+    },
+
+    /**
+     * Update the list of items, starting from the `_virtualStartVal` item.
+     * @param {!Array<number>=} itemSet
+     * @param {!Array<number>=} movingUp
+     */
+    _update: function(itemSet, movingUp) {
+      // update models
+      this._assignModels(itemSet);
+
+      // measure heights
+      this._updateMetrics(itemSet);
+
+      // adjust offset after measuring
+      if (movingUp) {
+        while (movingUp.length) {
+          this._physicalTop -= this._physicalSizes[movingUp.pop()];
+        }
+      }
+      // update the position of the items
+      this._positionItems();
+
+      // set the scroller size
+      this._updateScrollerSize();
+
+      // increase the pool of physical items if needed
+      if (this._increasePoolIfNeeded()) {
+        // set models to the new items
+        this.async(this._update);
+      }
+    },
+
+    /**
+     * Creates a pool of DOM elements and attaches them to the local dom.
+     */
+    _createPool: function(size) {
+      var physicalItems = new Array(size);
+
+      this._ensureTemplatized();
+
+      for (var i = 0; i < size; i++) {
+        var inst = this.stamp(null);
+
+        // First element child is item; Safari doesn't support children[0]
+        // on a doc fragment
+        physicalItems[i] = inst.root.querySelector('*');
+        Polymer.dom(this).appendChild(inst.root);
+      }
+
+      return physicalItems;
+    },
+
+    /**
+     * Increases the pool size. That is, the physical items in the DOM.
+     * This function will allocate additional physical items
+     * (limited by `MAX_PHYSICAL_COUNT`) if the content size is shorter than
+     * `_optPhysicalSize`
+     *
+     * @return boolean
+     */
+    _increasePoolIfNeeded: function() {
+      if (this._physicalSize >= this._optPhysicalSize || this._physicalAverage === 0) {
+        return false;
+      }
+
+      // the estimated number of physical items that we will need to reach
+      // the cap established by `_optPhysicalSize`.
+      var missingItems = Math.round(
+          (this._optPhysicalSize - this._physicalSize) * 1.2 / this._physicalAverage
+        );
+
+      // limit the size
+      var nextPhysicalCount = Math.min(
+          this._physicalCount + missingItems,
+          this._virtualCount,
+          MAX_PHYSICAL_COUNT
+        );
+
+      var prevPhysicalCount = this._physicalCount;
+      var delta = nextPhysicalCount - prevPhysicalCount;
+
+      if (delta <= 0) {
+        return false;
+      }
+
+      var newPhysicalItems = this._createPool(delta);
+      var emptyArray = new Array(delta);
+
+      [].push.apply(this._physicalItems, newPhysicalItems);
+      [].push.apply(this._physicalSizes, emptyArray);
+
+      this._physicalCount = prevPhysicalCount + delta;
+ 
+      return true;
+    },
+
+    /**
+     * Render a new list of items. This method does exactly the same as `update`,
+     * but it also ensures that only one `update` cycle is created.
+     */
+    _render: function() {
+      var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0;
+
+      if (this.isAttached && !this._itemsRendered && this._isVisible && requiresUpdate)  {
+        this._update();
+        this._itemsRendered = true;
+      }
+    },
+
+    /**
+     * Templetizes the user template.
+     */
+    _ensureTemplatized: function() {
+      if (!this.ctor) {
+        // Template instance props that should be excluded from forwarding
+        var props = {};
+
+        props.__key__ = true;
+        props[this.as] = true;
+        props[this.indexAs] = true;
+        props[this.selectedAs] = true;
+
+        this._instanceProps = props;
+        this._userTemplate = Polymer.dom(this).querySelector('template');
+
+        if (this._userTemplate) {
+          this.templatize(this._userTemplate);
+        } else {
+          console.warn('iron-list requires a template to be provided in light-dom');
+        }
+      }
+    },
+
+    /**
+     * Implements extension point from Templatizer mixin.
+     */
+    _getStampedChildren: function() {
+      return this._physicalItems;
+    },
+
+    /**
+     * Implements extension point from Templatizer
+     * Called as a side effect of a template instance path change, responsible
+     * for notifying items.<key-for-instance>.<path> change up to host.
+     */
+    _forwardInstancePath: function(inst, path, value) {
+      if (path.indexOf(this.as + '.') === 0) {
+        this.notifyPath('items.' + inst.__key__ + '.' +
+          path.slice(this.as.length + 1), value);
+      }
+    },
+
+    /**
+     * Implements extension point from Templatizer mixin
+     * Called as side-effect of a host property change, responsible for
+     * notifying parent path change on each row.
+     */
+    _forwardParentProp: function(prop, value) {
+      if (this._physicalItems) {
+        this._physicalItems.forEach(function(item) {
+          item._templateInstance[prop] = value;
+        }, this);
+      }
+    },
+
+    /**
+     * Implements extension point from Templatizer
+     * Called as side-effect of a host path change, responsible for
+     * notifying parent.<path> path change on each row.
+     */
+    _forwardParentPath: function(path, value) {
+      if (this._physicalItems) {
+        this._physicalItems.forEach(function(item) {
+          item._templateInstance.notifyPath(path, value, true);
+        }, this);
+      }
+    },
+
+    /**
+     * Called as a side effect of a host items.<key>.<path> path change,
+     * responsible for notifying item.<path> changes to row for key.
+     */
+    _forwardItemPath: function(path, value) {
+      if (this._physicalIndexForKey) {
+        var dot = path.indexOf('.');
+        var key = path.substring(0, dot < 0 ? path.length : dot);
+        var idx = this._physicalIndexForKey[key];
+        var row = this._physicalItems[idx];
+        if (row) {
+          var inst = row._templateInstance;
+          if (dot >= 0) {
+            path = this.as + '.' + path.substring(dot+1);
+            inst.notifyPath(path, value, true);
+          } else {
+            inst[this.as] = value;
+          }
+        }
+      }
+    },
+
+    /**
+     * Called when the items have changed. That is, ressignments
+     * to `items`, splices or updates to a single item.
+     */
+    _itemsChanged: function(change) {
+      if (change.path === 'items') {
+        // render the new set
+        this._itemsRendered = false;
+
+        // update the whole set
+        this._virtualStartVal = 0;
+        this._physicalTop = 0;
+        this._virtualCount = this.items ? this.items.length : 0;
+        this._collection = this.items ? Polymer.Collection.get(this.items) : null;
+        this._physicalIndexForKey = {};
+
+        // scroll to the top
+        this._resetScrollPosition(0);
+
+        // create the initial physical items
+        if (!this._physicalItems) {
+          this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, this._virtualCount));
+          this._physicalItems = this._createPool(this._physicalCount);
+          this._physicalSizes = new Array(this._physicalCount);
+        }
+
+        this.debounce('refresh', this._render);
+
+      } else if (change.path === 'items.splices') {
+        // render the new set
+        this._itemsRendered = false;
+
+        this._adjustVirtualIndex(change.value.indexSplices);
+        this._virtualCount = this.items ? this.items.length : 0;
+
+        this.debounce('refresh', this._render);
+
+      } else {
+        // update a single item
+        this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.value);
+      }
+    },
+
+    /**
+     * @param {!Array<!PolymerSplice>} splices
+     */
+    _adjustVirtualIndex: function(splices) {
+      var i, splice, idx;
+
+      for (i = 0; i < splices.length; i++) {
+        splice = splices[i];
+
+        // deselect removed items
+        splice.removed.forEach(this.$.selector.deselect, this.$.selector);
+
+        idx = splice.index;
+        // We only need to care about changes happening above the current position
+        if (idx >= this._virtualStartVal) {
+          break;
+        }
+
+        this._virtualStart = this._virtualStart +
+            Math.max(splice.addedCount - splice.removed.length, idx - this._virtualStartVal);
+      }
+    },
+
+    _scrollHandler: function() {
+      this._refresh();
+    },
+
+    /**
+     * Executes a provided function per every physical index in `itemSet`
+     * `itemSet` default value is equivalent to the entire set of physical indexes.
+     * 
+     * @param {!function(number, number)} fn
+     * @param {!Array<number>=} itemSet
+     */
+    _iterateItems: function(fn, itemSet) {
+      var pidx, vidx, rtn, i;
+
+      if (arguments.length === 2 && itemSet) {
+        for (i = 0; i < itemSet.length; i++) {
+          pidx = itemSet[i];
+          if (pidx >= this._physicalStart) {
+            vidx = this._virtualStartVal + (pidx - this._physicalStart);
+          } else {
+            vidx = this._virtualStartVal + (this._physicalCount - this._physicalStart) + pidx;
+          }
+          if ((rtn = fn.call(this, pidx, vidx)) != null) {
+            return rtn;
+          }
+        }
+      } else {
+        pidx = this._physicalStart;
+        vidx = this._virtualStartVal;
+
+        for (; pidx < this._physicalCount; pidx++, vidx++) {
+          if ((rtn = fn.call(this, pidx, vidx)) != null) {
+            return rtn;
+          }
+        }
+
+        pidx = 0;
+
+        for (; pidx < this._physicalStart; pidx++, vidx++) {
+          if ((rtn = fn.call(this, pidx, vidx)) != null) {
+            return rtn;
+          }
+        }
+      }
+    },
+
+    /**
+     * Assigns the data models to a given set of items.
+     * @param {!Array<number>=} itemSet
+     */
+    _assignModels: function(itemSet) {
+      this._iterateItems(function(pidx, vidx) {
+        var el = this._physicalItems[pidx];
+        var inst = el._templateInstance;
+        var item = this.items && this.items[vidx];
+
+        if (item) {
+          inst[this.as] = item;
+          inst.__key__ = this._collection.getKey(item);
+          inst[this.selectedAs] =
+            /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item);
+          inst[this.indexAs] = vidx;
+          el.removeAttribute('hidden');
+          this._physicalIndexForKey[inst.__key__] = pidx;
+        } else {
+          inst.__key__ = null;
+          el.setAttribute('hidden', '');
+        }
+
+      }, itemSet);
+    },
+
+    /**
+     * Updates the height for a given set of items.
+     *
+     * @param {!Array<number>=} itemSet
+     */
+     _updateMetrics: function(itemSet) {
+      var newPhysicalSize = 0;
+      var oldPhysicalSize = 0;
+      var prevAvgCount = this._physicalAverageCount;
+      var prevPhysicalAvg = this._physicalAverage;
+      // Make sure we distributed all the physical items
+      // so we can measure them
+      Polymer.dom.flush();
+
+      this._iterateItems(function(pidx, vidx) {
+        oldPhysicalSize += this._physicalSizes[pidx] || 0;
+        this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
+        newPhysicalSize += this._physicalSizes[pidx];
+        this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
+      }, itemSet);
+
+      this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize;
+      this._viewportSize = this._scroller.offsetHeight;
+
+      // update the average if we measured something
+      if (this._physicalAverageCount !== prevAvgCount) {
+        this._physicalAverage = Math.round(
+            ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) /
+            this._physicalAverageCount);
+      }
+    },
+
+    /**
+     * Updates the position of the physical items.
+     */
+    _positionItems: function() {
+      this._adjustScrollPosition();
+
+      var y = this._physicalTop;
+
+      this._iterateItems(function(pidx) {
+
+        this.transform('translate3d(0, ' + y + 'px, 0)', this._physicalItems[pidx]);
+        y += this._physicalSizes[pidx];
+
+      });
+    },
+
+    /**
+     * Adjusts the scroll position when it was overestimated.
+     */
+    _adjustScrollPosition: function() {
+      var deltaHeight = this._virtualStartVal === 0 ? this._physicalTop :
+          Math.min(this._scrollPosition + this._physicalTop, 0);
+
+      if (deltaHeight) {
+        this._physicalTop = this._physicalTop - deltaHeight;
+
+        // juking scroll position during interial scrolling on iOS is no bueno
+        if (!IOS_TOUCH_SCROLLING) {
+          this._resetScrollPosition(this._scroller.scrollTop - deltaHeight);
+        }
+      }
+    },
+
+    /**
+     * Sets the position of the scroll.
+     */
+    _resetScrollPosition: function(pos) {
+      if (this._scroller) {
+        this._scroller.scrollTop = pos;
+        this._scrollPosition = this._scroller.scrollTop;
+      }
+    },
+
+    /**
+     * Sets the scroll height, that's the height of the content,
+     * 
+     * @param {boolean=} forceUpdate If true, updates the height no matter what.
+     */
+    _updateScrollerSize: function(forceUpdate) {
+      this._estScrollHeight = (this._physicalBottom +
+          Math.max(this._virtualCount - this._physicalCount - this._virtualStartVal, 0) * this._physicalAverage);
+
+      forceUpdate = forceUpdate || this._scrollHeight === 0;
+      forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize;
+
+      // amortize height adjustment, so it won't trigger repaints very often
+      if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) {
+        this.$.items.style.height = this._estScrollHeight + 'px';
+        this._scrollHeight = this._estScrollHeight;
+      }
+    },
+
+    /**
+     * Scroll to a specific item in the virtual list regardless
+     * of the physical items in the DOM tree.
+     *
+     * @method scrollToIndex
+     * @param {number} idx The index of the item
+     */
+    scrollToIndex: function(idx) {
+      if (typeof idx !== 'number') {
+        return;
+      }
+
+      var firstVisible = this.firstVisibleIndex;
+
+      idx = Math.min(Math.max(idx, 0), this._virtualCount-1);
+
+      // start at the previous virtual item
+      // so we have a item above the first visible item
+      this._virtualStart = idx - 1;
+
+      // assign new models
+      this._assignModels();
+
+      // measure the new sizes
+      this._updateMetrics();
+
+      // estimate new physical offset
+      this._physicalTop = this._virtualStart * this._physicalAverage;
+
+      var currentTopItem = this._physicalStart;
+      var currentVirtualItem = this._virtualStart;
+      var targetOffsetTop = 0;
+      var hiddenContentSize = this._hiddenContentSize;
+
+      // scroll to the item as much as we can
+      while (currentVirtualItem !== idx && targetOffsetTop < hiddenContentSize) {
+        targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem];
+        currentTopItem = (currentTopItem + 1) % this._physicalCount;
+        currentVirtualItem++;
+      }
+
+      // update the scroller size
+      this._updateScrollerSize(true);
+
+      // update the position of the items
+      this._positionItems();
+
+      // set the new scroll position
+      this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1);
+
+      // increase the pool of physical items if needed
+      if (this._increasePoolIfNeeded()) {
+        // set models to the new items
+        this.async(this._update);
+      }
+
+      // clear cached visible index
+      this._firstVisibleIndexVal = null;
+    },
+
+    /**
+     * Reset the physical average and the average count.
+     */
+    _resetAverage: function() {
+      this._physicalAverage = 0;
+      this._physicalAverageCount = 0;
+    },
+
+    /**
+     * A handler for the `iron-resize` event triggered by `IronResizableBehavior`
+     * when the element is resized.
+     */
+    _resizeHandler: function() {
+      this.debounce('resize', function() {
+        this._render();
+        if (this._itemsRendered && this._physicalItems && this._isVisible) {
+          this._resetAverage();
+          this.updateViewportBoundaries();
+          this.scrollToIndex(this.firstVisibleIndex);
+        }
+      });
+    },
+
+    _getModelFromItem: function(item) {
+      var key = this._collection.getKey(item);
+      var pidx = this._physicalIndexForKey[key];
+
+      if (pidx !== undefined) {
+        return this._physicalItems[pidx]._templateInstance;
+      }
+      return null;
+    },
+
+    /**
+     * Gets a valid item instance from its index or the object value.
+     *
+     * @param {(Object|number)} item The item object or its index
+     */
+    _getNormalizedItem: function(item) {
+      if (typeof item === 'number') {
+        item = this.items[item];
+        if (!item) {
+          throw new RangeError('<item> not found');
+        }
+      } else if (this._collection.getKey(item) === undefined) {
+        throw new TypeError('<item> should be a valid item');
+      }
+      return item;
+    },
+
+    /**
+     * Select the list item at the given index.
+     *
+     * @method selectItem
+     * @param {(Object|number)} item The item object or its index
+     */
+    selectItem: function(item) {
+      item = this._getNormalizedItem(item);
+      var model = this._getModelFromItem(item);
+
+      if (!this.multiSelection && this.selectedItem) {
+        this.deselectItem(this.selectedItem);
+      }
+      if (model) {
+        model[this.selectedAs] = true;
+      }
+      this.$.selector.select(item);
+    },
+
+    /**
+     * Deselects the given item list if it is already selected.
+     *
+
+     * @method deselect
+     * @param {(Object|number)} item The item object or its index
+     */
+    deselectItem: function(item) {
+      item = this._getNormalizedItem(item);
+      var model = this._getModelFromItem(item);
+
+      if (model) {
+        model[this.selectedAs] = false;
+      }
+      this.$.selector.deselect(item);
+    },
+
+    /**
+     * Select or deselect a given item depending on whether the item
+     * has already been selected.
+     *
+     * @method toggleSelectionForItem
+     * @param {(Object|number)} item The item object or its index
+     */
+    toggleSelectionForItem: function(item) {
+      item = this._getNormalizedItem(item);
+      if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item)) {
+        this.deselectItem(item);
+      } else {
+        this.selectItem(item);
+      }
+    },
+
+    /**
+     * Clears the current selection state of the list.
+     *
+     * @method clearSelection
+     */
+    clearSelection: function() {
+      function unselect(item) {
+        var model = this._getModelFromItem(item);
+        if (model) {
+          model[this.selectedAs] = false;
+        }
+      }
+
+      if (Array.isArray(this.selectedItems)) {
+        this.selectedItems.forEach(unselect, this);
+      } else if (this.selectedItem) {
+        unselect.call(this, this.selectedItem);
+      }
+
+      /** @type {!ArraySelectorElement} */ (this.$.selector).clearSelection();
+    },
+
+    /**
+     * Add an event listener to `tap` if `selectionEnabled` is true,
+     * it will remove the listener otherwise.
+     */
+    _selectionEnabledChanged: function(selectionEnabled) {
+      if (selectionEnabled) {
+        this.listen(this, 'tap', '_selectionHandler');
+        this.listen(this, 'keypress', '_selectionHandler');
+      } else {
+        this.unlisten(this, 'tap', '_selectionHandler');
+        this.unlisten(this, 'keypress', '_selectionHandler');
+      }
+    },
+
+    /**
+     * Select an item from an event object.
+     */
+    _selectionHandler: function(e) {
+      if (e.type !== 'keypress' || e.keyCode === 13) {
+        var model = this.modelForElement(e.target);
+        if (model) {
+          this.toggleSelectionForItem(model[this.as]);
+        }
+      }
+    },
+
+    _multiSelectionChanged: function(multiSelection) {
+      this.clearSelection();
+      this.$.selector.multi = multiSelection;
+    },
+
+    /**
+     * Updates the size of an item.
+     *
+     * @method updateSizeForItem
+     * @param {(Object|number)} item The item object or its index
+     */
+    updateSizeForItem: function(item) {
+      item = this._getNormalizedItem(item);
+      var key = this._collection.getKey(item);
+      var pidx = this._physicalIndexForKey[key];
+
+      if (pidx !== undefined) {
+        this._updateMetrics([pidx]);
+        this._positionItems();
+      }
+    }
+  });
+
+})();
 (function() {
 
     'use strict';
@@ -9018,6 +10324,535 @@
     });
 
   })();
+(function() {
+
+    // monostate data
+    var metaDatas = {};
+    var metaArrays = {};
+
+    Polymer.IronMeta = Polymer({
+
+      is: 'iron-meta',
+
+      properties: {
+
+        /**
+         * The type of meta-data.  All meta-data of the same type is stored
+         * together.
+         */
+        type: {
+          type: String,
+          value: 'default',
+          observer: '_typeChanged'
+        },
+
+        /**
+         * The key used to store `value` under the `type` namespace.
+         */
+        key: {
+          type: String,
+          observer: '_keyChanged'
+        },
+
+        /**
+         * The meta-data to store or retrieve.
+         */
+        value: {
+          type: Object,
+          notify: true,
+          observer: '_valueChanged'
+        },
+
+        /**
+         * If true, `value` is set to the iron-meta instance itself.
+         */
+         self: {
+          type: Boolean,
+          observer: '_selfChanged'
+        },
+
+        /**
+         * Array of all meta-data values for the given type.
+         */
+        list: {
+          type: Array,
+          notify: true
+        }
+
+      },
+
+      /**
+       * Only runs if someone invokes the factory/constructor directly
+       * e.g. `new Polymer.IronMeta()`
+       */
+      factoryImpl: function(config) {
+        if (config) {
+          for (var n in config) {
+            switch(n) {
+              case 'type':
+              case 'key':
+              case 'value':
+                this[n] = config[n];
+                break;
+            }
+          }
+        }
+      },
+
+      created: function() {
+        // TODO(sjmiles): good for debugging?
+        this._metaDatas = metaDatas;
+        this._metaArrays = metaArrays;
+      },
+
+      _keyChanged: function(key, old) {
+        this._resetRegistration(old);
+      },
+
+      _valueChanged: function(value) {
+        this._resetRegistration(this.key);
+      },
+
+      _selfChanged: function(self) {
+        if (self) {
+          this.value = this;
+        }
+      },
+
+      _typeChanged: function(type) {
+        this._unregisterKey(this.key);
+        if (!metaDatas[type]) {
+          metaDatas[type] = {};
+        }
+        this._metaData = metaDatas[type];
+        if (!metaArrays[type]) {
+          metaArrays[type] = [];
+        }
+        this.list = metaArrays[type];
+        this._registerKeyValue(this.key, this.value);
+      },
+
+      /**
+       * Retrieves meta data value by key.
+       *
+       * @method byKey
+       * @param {string} key The key of the meta-data to be returned.
+       * @return {*}
+       */
+      byKey: function(key) {
+        return this._metaData && this._metaData[key];
+      },
+
+      _resetRegistration: function(oldKey) {
+        this._unregisterKey(oldKey);
+        this._registerKeyValue(this.key, this.value);
+      },
+
+      _unregisterKey: function(key) {
+        this._unregister(key, this._metaData, this.list);
+      },
+
+      _registerKeyValue: function(key, value) {
+        this._register(key, value, this._metaData, this.list);
+      },
+
+      _register: function(key, value, data, list) {
+        if (key && data && value !== undefined) {
+          data[key] = value;
+          list.push(value);
+        }
+      },
+
+      _unregister: function(key, data, list) {
+        if (key && data) {
+          if (key in data) {
+            var value = data[key];
+            delete data[key];
+            this.arrayDelete(list, value);
+          }
+        }
+      }
+
+    });
+
+    /**
+    `iron-meta-query` can be used to access infomation stored in `iron-meta`.
+
+    Examples:
+
+    If I create an instance like this:
+
+        <iron-meta key="info" value="foo/bar"></iron-meta>
+
+    Note that value="foo/bar" is the metadata I've defined. I could define more
+    attributes or use child nodes to define additional metadata.
+
+    Now I can access that element (and it's metadata) from any `iron-meta-query` instance:
+
+         var value = new Polymer.IronMetaQuery({key: 'info'}).value;
+
+    @group Polymer Iron Elements
+    @element iron-meta-query
+    */
+    Polymer.IronMetaQuery = Polymer({
+
+      is: 'iron-meta-query',
+
+      properties: {
+
+        /**
+         * The type of meta-data.  All meta-data of the same type is stored
+         * together.
+         */
+        type: {
+          type: String,
+          value: 'default',
+          observer: '_typeChanged'
+        },
+
+        /**
+         * Specifies a key to use for retrieving `value` from the `type`
+         * namespace.
+         */
+        key: {
+          type: String,
+          observer: '_keyChanged'
+        },
+
+        /**
+         * The meta-data to store or retrieve.
+         */
+        value: {
+          type: Object,
+          notify: true,
+          readOnly: true
+        },
+
+        /**
+         * Array of all meta-data values for the given type.
+         */
+        list: {
+          type: Array,
+          notify: true
+        }
+
+      },
+
+      /**
+       * Actually a factory method, not a true constructor. Only runs if
+       * someone invokes it directly (via `new Polymer.IronMeta()`);
+       */
+      factoryImpl: function(config) {
+        if (config) {
+          for (var n in config) {
+            switch(n) {
+              case 'type':
+              case 'key':
+                this[n] = config[n];
+                break;
+            }
+          }
+        }
+      },
+
+      created: function() {
+        // TODO(sjmiles): good for debugging?
+        this._metaDatas = metaDatas;
+        this._metaArrays = metaArrays;
+      },
+
+      _keyChanged: function(key) {
+        this._setValue(this._metaData && this._metaData[key]);
+      },
+
+      _typeChanged: function(type) {
+        this._metaData = metaDatas[type];
+        this.list = metaArrays[type];
+        if (this.key) {
+          this._keyChanged(this.key);
+        }
+      },
+
+      /**
+       * Retrieves meta data value by key.
+       * @param {string} key The key of the meta-data to be returned.
+       * @return {*}
+       */
+      byKey: function(key) {
+        return this._metaData && this._metaData[key];
+      }
+
+    });
+
+  })();
+Polymer({
+
+      is: 'iron-icon',
+
+      properties: {
+
+        /**
+         * The name of the icon to use. The name should be of the form:
+         * `iconset_name:icon_name`.
+         */
+        icon: {
+          type: String,
+          observer: '_iconChanged'
+        },
+
+        /**
+         * The name of the theme to used, if one is specified by the
+         * iconset.
+         */
+        theme: {
+          type: String,
+          observer: '_updateIcon'
+        },
+
+        /**
+         * If using iron-icon without an iconset, you can set the src to be
+         * the URL of an individual icon image file. Note that this will take
+         * precedence over a given icon attribute.
+         */
+        src: {
+          type: String,
+          observer: '_srcChanged'
+        },
+
+        /**
+         * @type {!Polymer.IronMeta}
+         */
+        _meta: {
+          value: Polymer.Base.create('iron-meta', {type: 'iconset'})
+        }
+
+      },
+
+      _DEFAULT_ICONSET: 'icons',
+
+      _iconChanged: function(icon) {
+        var parts = (icon || '').split(':');
+        this._iconName = parts.pop();
+        this._iconsetName = parts.pop() || this._DEFAULT_ICONSET;
+        this._updateIcon();
+      },
+
+      _srcChanged: function(src) {
+        this._updateIcon();
+      },
+
+      _usesIconset: function() {
+        return this.icon || !this.src;
+      },
+
+      /** @suppress {visibility} */
+      _updateIcon: function() {
+        if (this._usesIconset()) {
+          if (this._iconsetName) {
+            this._iconset = /** @type {?Polymer.Iconset} */ (
+              this._meta.byKey(this._iconsetName));
+            if (this._iconset) {
+              this._iconset.applyIcon(this, this._iconName, this.theme);
+              this.unlisten(window, 'iron-iconset-added', '_updateIcon');
+            } else {
+              this.listen(window, 'iron-iconset-added', '_updateIcon');
+            }
+          }
+        } else {
+          if (!this._img) {
+            this._img = document.createElement('img');
+            this._img.style.width = '100%';
+            this._img.style.height = '100%';
+            this._img.draggable = false;
+          }
+          this._img.src = this.src;
+          Polymer.dom(this.root).appendChild(this._img);
+        }
+      }
+
+    });
+/**
+   * 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
+   * `iron-iconset-svg` element. Multiple icons should be given distinct id's.
+   *
+   * Using svg elements to create icons has a few advantages over traditional
+   * bitmap graphics like jpg or png. Icons that use svg are vector based so they
+   * are resolution independent and should look good on any device. They are
+   * stylable via css. Icons can be themed, colorized, and even animated.
+   *
+   * Example:
+   *
+   *     <iron-iconset-svg name="my-svg-icons" size="24">
+   *       <svg>
+   *         <defs>
+   *           <g id="shape">
+   *             <rect x="50" y="50" width="50" height="50" />
+   *             <circle cx="50" cy="50" r="50" />
+   *           </g>
+   *         </defs>
+   *       </svg>
+   *     </iron-iconset-svg>
+   *
+   * This will automatically register the icon set "my-svg-icons" to the iconset
+   * database.  To use these icons from within another element, make a
+   * `iron-iconset` element and call the `byId` method
+   * to retrieve a given iconset. To apply a particular icon inside an
+   * element use the `applyIcon` method. For example:
+   *
+   *     iconset.applyIcon(iconNode, 'car');
+   *
+   * @element iron-iconset-svg
+   * @demo demo/index.html
+   */
+  Polymer({
+
+    is: 'iron-iconset-svg',
+
+    properties: {
+
+      /**
+       * The name of the iconset.
+       *
+       * @attribute name
+       * @type string
+       */
+      name: {
+        type: String,
+        observer: '_nameChanged'
+      },
+
+      /**
+       * The size of an individual icon. Note that icons must be square.
+       *
+       * @attribute iconSize
+       * @type number
+       * @default 24
+       */
+      size: {
+        type: Number,
+        value: 24
+      }
+
+    },
+
+    /**
+     * Construct an array of all icon names in this iconset.
+     *
+     * @return {!Array} Array of icon names.
+     */
+    getIconNames: function() {
+      this._icons = this._createIconMap();
+      return Object.keys(this._icons).map(function(n) {
+        return this.name + ':' + n;
+      }, this);
+    },
+
+    /**
+     * Applies an icon to the given element.
+     *
+     * An svg icon is prepended to the element's shadowRoot if it exists,
+     * otherwise to the element itself.
+     *
+     * @method applyIcon
+     * @param {Element} element Element to which the icon is applied.
+     * @param {string} iconName Name of the icon to apply.
+     * @return {Element} The svg element which renders the icon.
+     */
+    applyIcon: function(element, iconName) {
+      // insert svg element into shadow root, if it exists
+      element = element.root || element;
+      // Remove old svg element
+      this.removeIcon(element);
+      // install new svg element
+      var svg = this._cloneIcon(iconName);
+      if (svg) {
+        var pde = Polymer.dom(element);
+        pde.insertBefore(svg, pde.childNodes[0]);
+        return element._svgIcon = svg;
+      }
+      return null;
+    },
+
+    /**
+     * 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.
+     */
+    removeIcon: function(element) {
+      // Remove old svg element
+      if (element._svgIcon) {
+        Polymer.dom(element).removeChild(element._svgIcon);
+        element._svgIcon = null;
+      }
+    },
+
+    /**
+     *
+     * When name is changed, register iconset metadata
+     *
+     */
+    _nameChanged: function() {
+      new Polymer.IronMeta({type: 'iconset', key: this.name, value: this});
+      this.async(function() {
+        this.fire('iron-iconset-added', this, {node: window});
+      });
+    },
+
+    /**
+     * Create a map of child SVG elements by id.
+     *
+     * @return {!Object} Map of id's to SVG elements.
+     */
+    _createIconMap: function() {
+      // Objects chained to Object.prototype (`{}`) have members. Specifically,
+      // on FF there is a `watch` method that confuses the icon map, so we
+      // need to use a null-based object here.
+      var icons = Object.create(null);
+      Polymer.dom(this).querySelectorAll('[id]')
+        .forEach(function(icon) {
+          icons[icon.id] = icon;
+        });
+      return icons;
+    },
+
+    /**
+     * Produce installable clone of the SVG element matching `id` in this
+     * iconset, or `undefined` if there is no matching element.
+     *
+     * @return {Element} Returns an installable clone of the SVG element
+     * matching `id`.
+     */
+    _cloneIcon: function(id) {
+      // create the icon map on-demand, since the iconset itself has no discrete
+      // signal to know when it's children are fully parsed
+      this._icons = this._icons || this._createIconMap();
+      return this._prepareSvgClone(this._icons[id], this.size);
+    },
+
+    /**
+     * @param {Element} sourceSvg
+     * @param {number} size
+     * @return {Element}
+     */
+    _prepareSvgClone: function(sourceSvg, size) {
+      if (sourceSvg) {
+        var content = sourceSvg.cloneNode(true),
+            svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
+            viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size;
+        svg.setAttribute('viewBox', viewBox);
+        svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
+        // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136
+        // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root
+        svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;';
+        svg.appendChild(content).removeAttribute('id');
+        return svg;
+      }
+      return null;
+    }
+
+  });
 Polymer({
     is: 'paper-material',
 
@@ -9838,6 +11673,17 @@
           observer: '_holdDownChanged'
         },
 
+        /**
+         * If true, the ripple will not generate a ripple effect 
+         * via pointer interaction.
+         * Calling ripple's imperative api like `simulatedRipple` will 
+         * still generate the ripple effect.
+         */
+        noink: {
+          type: Boolean,
+          value: false
+        },
+
         _animating: {
           type: Boolean
         },
@@ -9850,6 +11696,10 @@
         }
       },
 
+      observers: [
+        '_noinkChanged(noink, isAttached)'
+      ],
+
       get target () {
         var ownerRoot = Polymer.dom(this).getOwnerRoot();
         var target;
@@ -9870,12 +11720,13 @@
       },
 
       attached: function() {
-        this.listen(this.target, 'up', 'upAction');
-        this.listen(this.target, 'down', 'downAction');
+        this.listen(this.target, 'up', 'uiUpAction');
+        this.listen(this.target, 'down', 'uiDownAction');
+      },
 
-        if (!this.target.hasAttribute('noink')) {
-          this.keyEventTarget = this.target;
-        }
+      detached: function() {
+        this.unlisten(this.target, 'up', 'uiUpAction');
+        this.unlisten(this.target, 'down', 'uiDownAction');
       },
 
       get shouldKeepAnimating () {
@@ -9897,7 +11748,22 @@
         }, 1);
       },
 
-      /** @param {Event=} event */
+      /** 
+       * Provokes a ripple down effect via a UI event, 
+       * respecting the `noink` property.
+       * @param {Event=} event 
+       */
+      uiDownAction: function(event) {
+        if (!this.noink) {
+          this.downAction(event);
+        }
+      },
+
+      /** 
+       * Provokes a ripple down effect via a UI event, 
+       * *not* respecting the `noink` property.
+       * @param {Event=} event 
+       */
       downAction: function(event) {
         if (this.holdDown && this.ripples.length > 0) {
           return;
@@ -9912,7 +11778,22 @@
         }
       },
 
-      /** @param {Event=} event */
+      /** 
+       * Provokes a ripple up effect via a UI event, 
+       * respecting the `noink` property.
+       * @param {Event=} event 
+       */
+      uiUpAction: function(event) {
+        if (!this.noink) {
+          this.upAction(event);
+        }
+      },
+
+      /** 
+       * Provokes a ripple up effect via a UI event, 
+       * *not* respecting the `noink` property.
+       * @param {Event=} event 
+       */
       upAction: function(event) {
         if (this.holdDown) {
           return;
@@ -9985,24 +11866,35 @@
       },
 
       _onEnterKeydown: function() {
-        this.downAction();
-        this.async(this.upAction, 1);
+        this.uiDownAction();
+        this.async(this.uiUpAction, 1);
       },
 
       _onSpaceKeydown: function() {
-        this.downAction();
+        this.uiDownAction();
       },
 
       _onSpaceKeyup: function() {
-        this.upAction();
+        this.uiUpAction();
       },
 
-      _holdDownChanged: function(holdDown) {
-        if (holdDown) {
+      // note: holdDown does not respect noink since it can be a focus based
+      // effect.
+      _holdDownChanged: function(newVal, oldVal) {
+        if (oldVal === undefined) {
+          return;
+        }
+        if (newVal) {
           this.downAction();
         } else {
           this.upAction();
         }
+      },
+
+      _noinkChanged: function(noink, attached) {
+        if (attached) {
+          this.keyEventTarget = noink ? this : this.target;
+        }
       }
     });
   })();
@@ -10208,33 +12100,7 @@
       }
     },
 
-    _eventSourceIsPrimaryInput: function(event) {
-      event = event.detail.sourceEvent || event;
-
-      // Always true for non-mouse events....
-      if (!this._mouseEventRe.test(event.type)) {
-        return true;
-      }
-
-      // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
-      if ('buttons' in event) {
-        return event.buttons === 1;
-      }
-
-      // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
-      if (typeof event.which === 'number') {
-        return event.which < 2;
-      }
-
-      // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
-      return event.button < 1;
-    },
-
     _downHandler: function(event) {
-      if (!this._eventSourceIsPrimaryInput(event)) {
-        return;
-      }
-
       this._setPointerDown(true);
       this._setPressed(true);
       this._setReceivedFocusFromKeyboard(false);
@@ -10312,24 +12178,138 @@
     Polymer.IronA11yKeysBehavior,
     Polymer.IronButtonStateImpl
   ];
-/** @polymerBehavior */
+/** 
+   * `Polymer.PaperRippleBehavior` dynamically implements a ripple 
+   * when the element has focus via pointer or keyboard.
+   *
+   * NOTE: This behavior is intended to be used in conjunction with and after
+   * `Polymer.IronButtonState` and `Polymer.IronControlState`.
+   *
+   * @polymerBehavior Polymer.PaperRippleBehavior 
+   */
+  Polymer.PaperRippleBehavior = {
+
+    properties: {
+      /**
+       * If true, the element will not produce a ripple effect when interacted
+       * with via the pointer.
+       */
+      noink: {
+        type: Boolean,
+        observer: '_noinkChanged'
+      }
+    },
+
+    /**
+     * Ensures a `<paper-ripple>` element is available when the element is 
+     * focused.
+     */
+    _buttonStateChanged: function() {
+      if (this.focused) {
+        this.ensureRipple();
+      }
+    },
+
+    /** 
+     * In addition to the functionality provided in `IronButtonState`, ensures
+     * a ripple effect is created when the element is in a `pressed` state.
+     */
+    _downHandler: function(event) {
+      Polymer.IronButtonStateImpl._downHandler.call(this, event);
+      if (this.pressed) {
+        this.ensureRipple(event);
+      }
+    },
+
+    /**
+     * Ensures this element contains a ripple effect. For startup efficiency 
+     * the ripple effect is dynamically on demand when needed.
+     * @param {!Event=} opt_triggeringEvent (optional) event that triggered the
+     * ripple.
+     */
+    ensureRipple: function(opt_triggeringEvent) {
+      if (!this.hasRipple()) {
+        this._ripple = this._createRipple();
+        this._ripple.noink = this.noink;
+        var rippleContainer = this._rippleContainer || this.root;
+        if (rippleContainer) {
+          Polymer.dom(rippleContainer).appendChild(this._ripple);
+        }
+        var domContainer = rippleContainer === this.shadyRoot ? this : 
+          rippleContainer;
+        if (opt_triggeringEvent &&
+            domContainer.contains(opt_triggeringEvent.target)) {
+          this._ripple.uiDownAction(opt_triggeringEvent);
+        }
+      }
+    },
+
+    /**
+     * Returns the `<paper-ripple>` element used by this element to create
+     * ripple effects. The element's ripple is created on demand, when
+     * necessary, and calling this method will force the 
+     * ripple to be created.
+     */
+    getRipple: function() {
+      this.ensureRipple();
+      return this._ripple;
+    },
+
+    /**
+     * Returns true if this element currently contains a ripple effect.
+     * @return {boolean}
+     */
+    hasRipple: function() {
+      return Boolean(this._ripple);
+    },
+
+    /**
+     * Create the element's ripple effect via creating a `<paper-ripple>`.
+     * Override this method to customize the ripple element.
+     * @return {element} Returns a `<paper-ripple>` element.
+     */
+    _createRipple: function() {
+      return document.createElement('paper-ripple');
+    },
+
+    _noinkChanged: function(noink) {
+      if (this.hasRipple()) {
+        this._ripple.noink = noink;
+      }
+    }
+
+  };
+/** @polymerBehavior Polymer.PaperButtonBehavior */
   Polymer.PaperButtonBehaviorImpl = {
 
     properties: {
 
-      _elevation: {
-        type: Number
+      /**
+       * The z-depth of this element, from 0-5. Setting to 0 will remove the
+       * shadow, and each increasing number greater than 0 will be "deeper"
+       * than the last.
+       *
+       * @attribute elevation
+       * @type number
+       * @default 1
+       */
+      elevation: {
+        type: Number,
+        reflectToAttribute: true,
+        readOnly: true
       }
 
     },
 
     observers: [
-      '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)'
+      '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)',
+      '_computeKeyboardClass(receivedFocusFromKeyboard)'
     ],
 
     hostAttributes: {
       role: 'button',
-      tabindex: '0'
+      tabindex: '0',
+      animated: true
     },
 
     _calculateElevation: function() {
@@ -10341,14 +12321,42 @@
       } else if (this.receivedFocusFromKeyboard) {
         e = 3;
       }
-      this._elevation = e;
+      this._setElevation(e);
+    },
+
+    _computeKeyboardClass: function(receivedFocusFromKeyboard) {
+      this.classList.toggle('keyboard-focus', receivedFocusFromKeyboard);
+    },
+
+    /**
+     * In addition to `IronButtonState` behavior, when space key goes down, 
+     * create a ripple down effect.
+     */
+    _spaceKeyDownHandler: function(event) {
+      Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event);
+      if (this.hasRipple()) {
+        this._ripple.uiDownAction();
+      }
+    },
+
+    /**
+     * In addition to `IronButtonState` behavior, when space key goes up, 
+     * create a ripple up effect.
+     */
+    _spaceKeyUpHandler: function(event) {
+      Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event);
+      if (this.hasRipple()) {
+        this._ripple.uiUpAction();
+      }
     }
+
   };
 
   /** @polymerBehavior */
   Polymer.PaperButtonBehavior = [
     Polymer.IronButtonState,
     Polymer.IronControlState,
+    Polymer.PaperRippleBehavior,
     Polymer.PaperButtonBehaviorImpl
   ];
 Polymer({
@@ -10372,18 +12380,10 @@
 
     _calculateElevation: function() {
       if (!this.raised) {
-        this._elevation = 0;
+        this.elevation = 0;
       } else {
         Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this);
       }
-    },
-
-    _computeContentClass: function(receivedFocusFromKeyboard) {
-      var className = 'content ';
-      if (receivedFocusFromKeyboard) {
-        className += ' keyboard-focus';
-      }
-      return className;
     }
   });
 /** 
@@ -10587,14 +12587,6 @@
   var Item = Polymer({
     is: 'downloads-item',
 
-    /**
-     * @param {!downloads.ThrottledIconLoader} iconLoader
-     */
-    factoryImpl: function(iconLoader) {
-      /** @private {!downloads.ThrottledIconLoader} */
-      this.iconLoader_ = iconLoader;
-    },
-
     properties: {
       data: {
         type: Object,
@@ -10605,15 +12597,6 @@
         value: true,
       },
 
-      readyPromise: {
-        type: Object,
-        value: function() {
-          return new Promise(function(resolve, reject) {
-            this.resolveReadyPromise_ = resolve;
-          }.bind(this));
-        },
-      },
-
       completelyOnDisk_: {
         computed: 'computeCompletelyOnDisk_(' +
             'data.state, data.file_externally_removed)',
@@ -10686,21 +12669,11 @@
       // TODO(dbeam): this gets called way more when I observe data.by_ext_id
       // and data.by_ext_name directly. Why?
       'observeControlledBy_(controlledBy_)',
+      'observeIsDangerous_(isDangerous_, data.file_path)',
     ],
 
     ready: function() {
       this.content = this.$.content;
-      this.resolveReadyPromise_();
-    },
-
-    /** @param {!downloads.Data} data */
-    update: function(data) {
-      this.data = data;
-
-      if (!this.isDangerous_) {
-        var icon = 'chrome://fileicon/' + encodeURIComponent(data.file_path);
-        this.iconLoader_.loadScaledIcon(this.$['file-icon'], icon);
-      }
     },
 
     /** @private */
@@ -10736,6 +12709,23 @@
     },
 
     /** @private */
+    computeDangerIcon_: function() {
+      if (!this.isDangerous_)
+        return '';
+
+      switch (this.data.danger_type) {
+        case downloads.DangerType.DANGEROUS_CONTENT:
+        case downloads.DangerType.DANGEROUS_HOST:
+        case downloads.DangerType.DANGEROUS_URL:
+        case downloads.DangerType.POTENTIALLY_UNWANTED:
+        case downloads.DangerType.UNCOMMON_CONTENT:
+          return 'remove-circle';
+        default:
+          return 'warning';
+      }
+    },
+
+    /** @private */
     computeDate_: function() {
       if (this.hideDate)
         return '';
@@ -10844,6 +12834,14 @@
     },
 
     /** @private */
+    observeIsDangerous_: function() {
+      if (this.data && !this.isDangerous_) {
+        var filePath = encodeURIComponent(this.data.file_path);
+        this.$['file-icon'].src = 'chrome://fileicon/' + filePath;
+      }
+    },
+
+    /** @private */
     onCancelTap_: function() {
       downloads.ActionService.getInstance().cancel(this.data.id);
     },
@@ -10904,533 +12902,6 @@
 
   return {Item: Item};
 });
-(function() {
-
-    // monostate data
-    var metaDatas = {};
-    var metaArrays = {};
-
-    Polymer.IronMeta = Polymer({
-
-      is: 'iron-meta',
-
-      properties: {
-
-        /**
-         * The type of meta-data.  All meta-data of the same type is stored
-         * together.
-         */
-        type: {
-          type: String,
-          value: 'default',
-          observer: '_typeChanged'
-        },
-
-        /**
-         * The key used to store `value` under the `type` namespace.
-         */
-        key: {
-          type: String,
-          observer: '_keyChanged'
-        },
-
-        /**
-         * The meta-data to store or retrieve.
-         */
-        value: {
-          type: Object,
-          notify: true,
-          observer: '_valueChanged'
-        },
-
-        /**
-         * If true, `value` is set to the iron-meta instance itself.
-         */
-         self: {
-          type: Boolean,
-          observer: '_selfChanged'
-        },
-
-        /**
-         * Array of all meta-data values for the given type.
-         */
-        list: {
-          type: Array,
-          notify: true
-        }
-
-      },
-
-      /**
-       * Only runs if someone invokes the factory/constructor directly
-       * e.g. `new Polymer.IronMeta()`
-       */
-      factoryImpl: function(config) {
-        if (config) {
-          for (var n in config) {
-            switch(n) {
-              case 'type':
-              case 'key':
-              case 'value':
-                this[n] = config[n];
-                break;
-            }
-          }
-        }
-      },
-
-      created: function() {
-        // TODO(sjmiles): good for debugging?
-        this._metaDatas = metaDatas;
-        this._metaArrays = metaArrays;
-      },
-
-      _keyChanged: function(key, old) {
-        this._resetRegistration(old);
-      },
-
-      _valueChanged: function(value) {
-        this._resetRegistration(this.key);
-      },
-
-      _selfChanged: function(self) {
-        if (self) {
-          this.value = this;
-        }
-      },
-
-      _typeChanged: function(type) {
-        this._unregisterKey(this.key);
-        if (!metaDatas[type]) {
-          metaDatas[type] = {};
-        }
-        this._metaData = metaDatas[type];
-        if (!metaArrays[type]) {
-          metaArrays[type] = [];
-        }
-        this.list = metaArrays[type];
-        this._registerKeyValue(this.key, this.value);
-      },
-
-      /**
-       * Retrieves meta data value by key.
-       *
-       * @method byKey
-       * @param {string} key The key of the meta-data to be returned.
-       * @return {*}
-       */
-      byKey: function(key) {
-        return this._metaData && this._metaData[key];
-      },
-
-      _resetRegistration: function(oldKey) {
-        this._unregisterKey(oldKey);
-        this._registerKeyValue(this.key, this.value);
-      },
-
-      _unregisterKey: function(key) {
-        this._unregister(key, this._metaData, this.list);
-      },
-
-      _registerKeyValue: function(key, value) {
-        this._register(key, value, this._metaData, this.list);
-      },
-
-      _register: function(key, value, data, list) {
-        if (key && data && value !== undefined) {
-          data[key] = value;
-          list.push(value);
-        }
-      },
-
-      _unregister: function(key, data, list) {
-        if (key && data) {
-          if (key in data) {
-            var value = data[key];
-            delete data[key];
-            this.arrayDelete(list, value);
-          }
-        }
-      }
-
-    });
-
-    /**
-    `iron-meta-query` can be used to access infomation stored in `iron-meta`.
-
-    Examples:
-
-    If I create an instance like this:
-
-        <iron-meta key="info" value="foo/bar"></iron-meta>
-
-    Note that value="foo/bar" is the metadata I've defined. I could define more
-    attributes or use child nodes to define additional metadata.
-
-    Now I can access that element (and it's metadata) from any `iron-meta-query` instance:
-
-         var value = new Polymer.IronMetaQuery({key: 'info'}).value;
-
-    @group Polymer Iron Elements
-    @element iron-meta-query
-    */
-    Polymer.IronMetaQuery = Polymer({
-
-      is: 'iron-meta-query',
-
-      properties: {
-
-        /**
-         * The type of meta-data.  All meta-data of the same type is stored
-         * together.
-         */
-        type: {
-          type: String,
-          value: 'default',
-          observer: '_typeChanged'
-        },
-
-        /**
-         * Specifies a key to use for retrieving `value` from the `type`
-         * namespace.
-         */
-        key: {
-          type: String,
-          observer: '_keyChanged'
-        },
-
-        /**
-         * The meta-data to store or retrieve.
-         */
-        value: {
-          type: Object,
-          notify: true,
-          readOnly: true
-        },
-
-        /**
-         * Array of all meta-data values for the given type.
-         */
-        list: {
-          type: Array,
-          notify: true
-        }
-
-      },
-
-      /**
-       * Actually a factory method, not a true constructor. Only runs if
-       * someone invokes it directly (via `new Polymer.IronMeta()`);
-       */
-      factoryImpl: function(config) {
-        if (config) {
-          for (var n in config) {
-            switch(n) {
-              case 'type':
-              case 'key':
-                this[n] = config[n];
-                break;
-            }
-          }
-        }
-      },
-
-      created: function() {
-        // TODO(sjmiles): good for debugging?
-        this._metaDatas = metaDatas;
-        this._metaArrays = metaArrays;
-      },
-
-      _keyChanged: function(key) {
-        this._setValue(this._metaData && this._metaData[key]);
-      },
-
-      _typeChanged: function(type) {
-        this._metaData = metaDatas[type];
-        this.list = metaArrays[type];
-        if (this.key) {
-          this._keyChanged(this.key);
-        }
-      },
-
-      /**
-       * Retrieves meta data value by key.
-       * @param {string} key The key of the meta-data to be returned.
-       * @return {*}
-       */
-      byKey: function(key) {
-        return this._metaData && this._metaData[key];
-      }
-
-    });
-
-  })();
-Polymer({
-
-      is: 'iron-icon',
-
-      properties: {
-
-        /**
-         * The name of the icon to use. The name should be of the form:
-         * `iconset_name:icon_name`.
-         */
-        icon: {
-          type: String,
-          observer: '_iconChanged'
-        },
-
-        /**
-         * The name of the theme to used, if one is specified by the
-         * iconset.
-         */
-        theme: {
-          type: String,
-          observer: '_updateIcon'
-        },
-
-        /**
-         * If using iron-icon without an iconset, you can set the src to be
-         * the URL of an individual icon image file. Note that this will take
-         * precedence over a given icon attribute.
-         */
-        src: {
-          type: String,
-          observer: '_srcChanged'
-        },
-
-        /**
-         * @type {!Polymer.IronMeta}
-         */
-        _meta: {
-          value: Polymer.Base.create('iron-meta', {type: 'iconset'})
-        }
-
-      },
-
-      _DEFAULT_ICONSET: 'icons',
-
-      _iconChanged: function(icon) {
-        var parts = (icon || '').split(':');
-        this._iconName = parts.pop();
-        this._iconsetName = parts.pop() || this._DEFAULT_ICONSET;
-        this._updateIcon();
-      },
-
-      _srcChanged: function(src) {
-        this._updateIcon();
-      },
-
-      _usesIconset: function() {
-        return this.icon || !this.src;
-      },
-
-      /** @suppress {visibility} */
-      _updateIcon: function() {
-        if (this._usesIconset()) {
-          if (this._iconsetName) {
-            this._iconset = /** @type {?Polymer.Iconset} */ (
-              this._meta.byKey(this._iconsetName));
-            if (this._iconset) {
-              this._iconset.applyIcon(this, this._iconName, this.theme);
-              this.unlisten(window, 'iron-iconset-added', '_updateIcon');
-            } else {
-              this.listen(window, 'iron-iconset-added', '_updateIcon');
-            }
-          }
-        } else {
-          if (!this._img) {
-            this._img = document.createElement('img');
-            this._img.style.width = '100%';
-            this._img.style.height = '100%';
-            this._img.draggable = false;
-          }
-          this._img.src = this.src;
-          Polymer.dom(this.root).appendChild(this._img);
-        }
-      }
-
-    });
-/**
-   * 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
-   * `iron-iconset-svg` element. Multiple icons should be given distinct id's.
-   *
-   * Using svg elements to create icons has a few advantages over traditional
-   * bitmap graphics like jpg or png. Icons that use svg are vector based so they
-   * are resolution independent and should look good on any device. They are
-   * stylable via css. Icons can be themed, colorized, and even animated.
-   *
-   * Example:
-   *
-   *     <iron-iconset-svg name="my-svg-icons" size="24">
-   *       <svg>
-   *         <defs>
-   *           <g id="shape">
-   *             <rect x="50" y="50" width="50" height="50" />
-   *             <circle cx="50" cy="50" r="50" />
-   *           </g>
-   *         </defs>
-   *       </svg>
-   *     </iron-iconset-svg>
-   *
-   * This will automatically register the icon set "my-svg-icons" to the iconset
-   * database.  To use these icons from within another element, make a
-   * `iron-iconset` element and call the `byId` method
-   * to retrieve a given iconset. To apply a particular icon inside an
-   * element use the `applyIcon` method. For example:
-   *
-   *     iconset.applyIcon(iconNode, 'car');
-   *
-   * @element iron-iconset-svg
-   * @demo demo/index.html
-   */
-  Polymer({
-
-    is: 'iron-iconset-svg',
-
-    properties: {
-
-      /**
-       * The name of the iconset.
-       *
-       * @attribute name
-       * @type string
-       */
-      name: {
-        type: String,
-        observer: '_nameChanged'
-      },
-
-      /**
-       * The size of an individual icon. Note that icons must be square.
-       *
-       * @attribute iconSize
-       * @type number
-       * @default 24
-       */
-      size: {
-        type: Number,
-        value: 24
-      }
-
-    },
-
-    /**
-     * Construct an array of all icon names in this iconset.
-     *
-     * @return {!Array} Array of icon names.
-     */
-    getIconNames: function() {
-      this._icons = this._createIconMap();
-      return Object.keys(this._icons).map(function(n) {
-        return this.name + ':' + n;
-      }, this);
-    },
-
-    /**
-     * Applies an icon to the given element.
-     *
-     * An svg icon is prepended to the element's shadowRoot if it exists,
-     * otherwise to the element itself.
-     *
-     * @method applyIcon
-     * @param {Element} element Element to which the icon is applied.
-     * @param {string} iconName Name of the icon to apply.
-     * @return {Element} The svg element which renders the icon.
-     */
-    applyIcon: function(element, iconName) {
-      // insert svg element into shadow root, if it exists
-      element = element.root || element;
-      // Remove old svg element
-      this.removeIcon(element);
-      // install new svg element
-      var svg = this._cloneIcon(iconName);
-      if (svg) {
-        var pde = Polymer.dom(element);
-        pde.insertBefore(svg, pde.childNodes[0]);
-        return element._svgIcon = svg;
-      }
-      return null;
-    },
-
-    /**
-     * 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.
-     */
-    removeIcon: function(element) {
-      // Remove old svg element
-      if (element._svgIcon) {
-        Polymer.dom(element).removeChild(element._svgIcon);
-        element._svgIcon = null;
-      }
-    },
-
-    /**
-     *
-     * When name is changed, register iconset metadata
-     *
-     */
-    _nameChanged: function() {
-      new Polymer.IronMeta({type: 'iconset', key: this.name, value: this});
-      this.async(function() {
-        this.fire('iron-iconset-added', this, {node: window});
-      });
-    },
-
-    /**
-     * Create a map of child SVG elements by id.
-     *
-     * @return {!Object} Map of id's to SVG elements.
-     */
-    _createIconMap: function() {
-      // Objects chained to Object.prototype (`{}`) have members. Specifically,
-      // on FF there is a `watch` method that confuses the icon map, so we
-      // need to use a null-based object here.
-      var icons = Object.create(null);
-      Polymer.dom(this).querySelectorAll('[id]')
-        .forEach(function(icon) {
-          icons[icon.id] = icon;
-        });
-      return icons;
-    },
-
-    /**
-     * Produce installable clone of the SVG element matching `id` in this
-     * iconset, or `undefined` if there is no matching element.
-     *
-     * @return {Element} Returns an installable clone of the SVG element
-     * matching `id`.
-     */
-    _cloneIcon: function(id) {
-      // create the icon map on-demand, since the iconset itself has no discrete
-      // signal to know when it's children are fully parsed
-      this._icons = this._icons || this._createIconMap();
-      return this._prepareSvgClone(this._icons[id], this.size);
-    },
-
-    /**
-     * @param {Element} sourceSvg
-     * @param {number} size
-     * @return {Element}
-     */
-    _prepareSvgClone: function(sourceSvg, size) {
-      if (sourceSvg) {
-        var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
-        svg.setAttribute('viewBox', ['0', '0', size, size].join(' '));
-        svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
-        // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136
-        // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root
-        svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;';
-        svg.appendChild(sourceSvg.cloneNode(true)).removeAttribute('id');
-        return svg;
-      }
-      return null;
-    }
-
-  });
 Polymer({
       is: 'paper-item',
 
@@ -11642,7 +13113,7 @@
        * @type {object}
        * @default {template: 1}
        */
-      excludedLocalNames: {
+      _excludedLocalNames: {
         type: Object,
         value: function() {
           return {
@@ -11659,6 +13130,9 @@
     created: function() {
       this._bindFilterItem = this._filterItem.bind(this);
       this._selection = new Polymer.IronSelection(this._applySelection.bind(this));
+      // TODO(cdata): When polymer/polymer#2535 lands, we do not need to do this
+      // book keeping anymore:
+      this.__listeningForActivate = false;
     },
 
     attached: function() {
@@ -11667,6 +13141,7 @@
       if (!this.selectedItem && this.selected) {
         this._updateSelected(this.attrForSelected,this.selected)
       }
+      this._addListener(this.activateEvent);
     },
 
     detached: function() {
@@ -11733,11 +13208,17 @@
     },
 
     _addListener: function(eventName) {
+      if (!this.isAttached || this.__listeningForActivate) {
+        return;
+      }
+
+      this.__listeningForActivate = true;
       this.listen(this, eventName, '_activateHandler');
     },
 
     _removeListener: function(eventName) {
       this.unlisten(this, eventName, '_activateHandler');
+      this.__listeningForActivate = false;
     },
 
     _activateEventChanged: function(eventName, old) {
@@ -11754,7 +13235,7 @@
     },
 
     _filterItem: function(node) {
-      return !this.excludedLocalNames[node.localName];
+      return !this._excludedLocalNames[node.localName];
     },
 
     _valueToItem: function(value) {
@@ -12265,184 +13746,6 @@
 
 })();
 /**
-   * `IronResizableBehavior` is a behavior that can be used in Polymer elements to
-   * coordinate the flow of resize events between "resizers" (elements that control the
-   * size or hidden state of their children) and "resizables" (elements that need to be
-   * notified when they are resized or un-hidden by their parents in order to take
-   * action on their new measurements).
-   * Elements that perform measurement should add the `IronResizableBehavior` behavior to
-   * their element definition and listen for the `iron-resize` event on themselves.
-   * This event will be fired when they become showing after having been hidden,
-   * when they are resized explicitly by another resizable, or when the window has been
-   * resized.
-   * Note, the `iron-resize` event is non-bubbling.
-   *
-   * @polymerBehavior Polymer.IronResizableBehavior
-   * @demo demo/index.html
-   **/
-  Polymer.IronResizableBehavior = {
-    properties: {
-      /**
-       * The closest ancestor element that implements `IronResizableBehavior`.
-       */
-      _parentResizable: {
-        type: Object,
-        observer: '_parentResizableChanged'
-      },
-
-      /**
-       * True if this element is currently notifying its descedant elements of
-       * resize.
-       */
-      _notifyingDescendant: {
-        type: Boolean,
-        value: false
-      }
-    },
-
-    listeners: {
-      'iron-request-resize-notifications': '_onIronRequestResizeNotifications'
-    },
-
-    created: function() {
-      // We don't really need property effects on these, and also we want them
-      // to be created before the `_parentResizable` observer fires:
-      this._interestedResizables = [];
-      this._boundNotifyResize = this.notifyResize.bind(this);
-    },
-
-    attached: function() {
-      this.fire('iron-request-resize-notifications', null, {
-        node: this,
-        bubbles: true,
-        cancelable: true
-      });
-
-      if (!this._parentResizable) {
-        window.addEventListener('resize', this._boundNotifyResize);
-        this.notifyResize();
-      }
-    },
-
-    detached: function() {
-      if (this._parentResizable) {
-        this._parentResizable.stopResizeNotificationsFor(this);
-      } else {
-        window.removeEventListener('resize', this._boundNotifyResize);
-      }
-
-      this._parentResizable = null;
-    },
-
-    /**
-     * Can be called to manually notify a resizable and its descendant
-     * resizables of a resize change.
-     */
-    notifyResize: function() {
-      if (!this.isAttached) {
-        return;
-      }
-
-      this._interestedResizables.forEach(function(resizable) {
-        if (this.resizerShouldNotify(resizable)) {
-          this._notifyDescendant(resizable);
-        }
-      }, this);
-
-      this._fireResize();
-    },
-
-    /**
-     * Used to assign the closest resizable ancestor to this resizable
-     * if the ancestor detects a request for notifications.
-     */
-    assignParentResizable: function(parentResizable) {
-      this._parentResizable = parentResizable;
-    },
-
-    /**
-     * Used to remove a resizable descendant from the list of descendants
-     * that should be notified of a resize change.
-     */
-    stopResizeNotificationsFor: function(target) {
-      var index = this._interestedResizables.indexOf(target);
-
-      if (index > -1) {
-        this._interestedResizables.splice(index, 1);
-        this.unlisten(target, 'iron-resize', '_onDescendantIronResize');
-      }
-    },
-
-    /**
-     * This method can be overridden to filter nested elements that should or
-     * should not be notified by the current element. Return true if an element
-     * should be notified, or false if it should not be notified.
-     *
-     * @param {HTMLElement} element A candidate descendant element that
-     * implements `IronResizableBehavior`.
-     * @return {boolean} True if the `element` should be notified of resize.
-     */
-    resizerShouldNotify: function(element) { return true; },
-
-    _onDescendantIronResize: function(event) {
-      if (this._notifyingDescendant) {
-        event.stopPropagation();
-        return;
-      }
-
-      // NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the
-      // otherwise non-bubbling event "just work." We do it manually here for
-      // the case where Polymer is not using shadow roots for whatever reason:
-      if (!Polymer.Settings.useShadow) {
-        this._fireResize();
-      }
-    },
-
-    _fireResize: function() {
-      this.fire('iron-resize', null, {
-        node: this,
-        bubbles: false
-      });
-    },
-
-    _onIronRequestResizeNotifications: function(event) {
-      var target = event.path ? event.path[0] : event.target;
-
-      if (target === this) {
-        return;
-      }
-
-      if (this._interestedResizables.indexOf(target) === -1) {
-        this._interestedResizables.push(target);
-        this.listen(target, 'iron-resize', '_onDescendantIronResize');
-      }
-
-      target.assignParentResizable(this);
-      this._notifyDescendant(target);
-
-      event.stopPropagation();
-    },
-
-    _parentResizableChanged: function(parentResizable) {
-      if (parentResizable) {
-        window.removeEventListener('resize', this._boundNotifyResize);
-      }
-    },
-
-    _notifyDescendant: function(descendant) {
-      // NOTE(cdata): In IE10, attached is fired on children first, so it's
-      // important not to notify them if the parent is not attached yet (or
-      // else they will get redundantly notified when the parent attaches).
-      if (!this.isAttached) {
-        return;
-      }
-
-      this._notifyingDescendant = true;
-      descendant.notifyResize();
-      this._notifyingDescendant = false;
-    }
-  };
-/**
 Polymer.IronFitBehavior fits an element in another element using `max-height` and `max-width`, and
 optionally centers it in the window or another element.
 
@@ -12857,7 +14160,8 @@
 it will cancel whenever the user taps outside it or presses the escape key. This behavior is
 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties.
 `close()` should be called explicitly by the implementer when the user interacts with a control
-in the overlay element.
+in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled'
+event. Call `preventDefault` on this event to prevent the overlay from closing.
 
 ### Positioning
 
@@ -13028,6 +14332,11 @@
      * Cancels the overlay.
      */
     cancel: function() {
+      var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true});
+      if (cancelEvent.defaultPrevented) {
+        return;
+      }
+
       this.opened = false;
       this._setCanceled(true);
     },
@@ -14576,7 +15885,7 @@
 /**
    * `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus.
    *
-   * @polymerBehavior Polymer.PaperInkyFocusBehavior
+   * @polymerBehavior Polymer.PaperInkyFocusBehaviorImpl
    */
   Polymer.PaperInkyFocusBehaviorImpl = {
 
@@ -14585,11 +15894,20 @@
     ],
 
     _focusedChanged: function(receivedFocusFromKeyboard) {
-      if (!this.$.ink) {
-        return;
+      if (receivedFocusFromKeyboard) {
+        this.ensureRipple();
       }
+      if (this.hasRipple()) {
+        this._ripple.holdDown = receivedFocusFromKeyboard;
+      }
+    },
 
-      this.$.ink.holdDown = receivedFocusFromKeyboard;
+    _createRipple: function() {
+      var ripple = Polymer.PaperRippleBehavior._createRipple();
+      ripple.id = 'ink';
+      ripple.setAttribute('center', '');
+      ripple.classList.add('circle');
+      return ripple;
     }
 
   };
@@ -14598,56 +15916,57 @@
   Polymer.PaperInkyFocusBehavior = [
     Polymer.IronButtonState,
     Polymer.IronControlState,
+    Polymer.PaperRippleBehavior,
     Polymer.PaperInkyFocusBehaviorImpl
   ];
 Polymer({
-    is: 'paper-icon-button',
+      is: 'paper-icon-button',
 
-    hostAttributes: {
-      role: 'button',
-      tabindex: '0'
-    },
-
-    behaviors: [
-      Polymer.PaperInkyFocusBehavior
-    ],
-
-    properties: {
-      /**
-       * The URL of an image for the icon. If the src property is specified,
-       * the icon property should not be.
-       */
-      src: {
-        type: String
+      hostAttributes: {
+        role: 'button',
+        tabindex: '0'
       },
 
-      /**
-       * Specifies the icon name or index in the set of icons available in
-       * the icon's icon set. If the icon property is specified,
-       * the src property should not be.
-       */
-      icon: {
-        type: String
+      behaviors: [
+        Polymer.PaperInkyFocusBehavior
+      ],
+
+      properties: {
+        /**
+         * The URL of an image for the icon. If the src property is specified,
+         * the icon property should not be.
+         */
+        src: {
+          type: String
+        },
+
+        /**
+         * Specifies the icon name or index in the set of icons available in
+         * the icon's icon set. If the icon property is specified,
+         * the src property should not be.
+         */
+        icon: {
+          type: String
+        },
+
+        /**
+         * Specifies the alternate text for the button, for accessibility.
+         */
+        alt: {
+          type: String,
+          observer: "_altChanged"
+        }
       },
 
-      /**
-       * Specifies the alternate text for the button, for accessibility.
-       */
-      alt: {
-        type: String,
-        observer: "_altChanged"
-      }
-    },
+      _altChanged: function(newValue, oldValue) {
+        var label = this.getAttribute('aria-label');
 
-    _altChanged: function(newValue, oldValue) {
-      var label = this.getAttribute('aria-label');
-
-      // Don't stomp over a user-set aria-label.
-      if (!label || oldValue == label) {
-        this.setAttribute('aria-label', newValue);
+        // Don't stomp over a user-set aria-label.
+        if (!label || oldValue == label) {
+          this.setAttribute('aria-label', newValue);
+        }
       }
-    }
-  });
+    });
 /**
    * Use `Polymer.IronValidatableBehavior` to implement an element that validates user input.
    *
@@ -15445,15 +16764,10 @@
         type: Boolean,
         value: false,
       },
-    },
 
-    /**
-     * @return {number} A guess at how many items could be visible at once.
-     * @private
-     */
-    guesstimateNumberOfVisibleItems_: function() {
-      var toolbarHeight = this.$.toolbar.offsetHeight;
-      return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1;
+      items_: {
+        type: Array,
+      },
     },
 
     /**
@@ -15493,33 +16807,6 @@
       downloads.ActionService.getInstance().search('');
     },
 
-    /** @private */
-    rebuildFocusGrid_: function() {
-      var activeElement = this.shadowRoot.activeElement;
-
-      var activeItem;
-      if (activeElement && activeElement.tagName == 'downloads-item')
-        activeItem = activeElement;
-
-      var activeControl = activeItem && activeItem.shadowRoot.activeElement;
-
-      /** @private {!cr.ui.FocusGrid} */
-      this.focusGrid_ = this.focusGrid_ || new cr.ui.FocusGrid;
-      this.focusGrid_.destroy();
-
-      var boundary = this.$['downloads-list'];
-
-      this.items_.forEach(function(item) {
-        var focusRow = new downloads.FocusRow(item.content, boundary);
-        this.focusGrid_.addRow(focusRow);
-
-        if (item == activeItem && !cr.ui.FocusRow.isFocusable(activeControl))
-          focusRow.getEquivalentElement(activeControl).focus();
-      }, this);
-
-      this.focusGrid_.ensureRowActive();
-    },
-
     /**
      * @return {number} The number of downloads shown on the page.
      * @private
@@ -15534,62 +16821,24 @@
      * @private
      */
     updateAll_: function(list) {
-      var oldIdMap = this.idMap_ || {};
-
-      /** @private {!Object<!downloads.Item>} */
-      this.idMap_ = {};
-
-      /** @private {!Array<!downloads.Item>} */
-      this.items_ = [];
-
-      if (!this.iconLoader_) {
-        var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1);
-        /** @private {downloads.ThrottledIconLoader} */
-        this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate);
-      }
+      /** @private {!Object<number>} */
+      this.idToIndex_ = {};
 
       for (var i = 0; i < list.length; ++i) {
         var data = list[i];
-        var id = data.id;
 
-        // Re-use old items when possible (saves work, preserves focus).
-        var item = oldIdMap[id] || new downloads.Item(this.iconLoader_);
+        this.idToIndex_[data.id] = data.index = i;
 
-        this.idMap_[id] = item;  // Associated by ID for fast lookup.
-        this.items_.push(item);  // Add to sorted list for order.
-
-        // Render |item| but don't actually add to the DOM yet. |this.items_|
-        // must be fully created to be able to find the right spot to insert.
-        item.update(data);
-
-        // Collapse redundant dates.
         var prev = list[i - 1];
-        item.hideDate = !!prev && prev.date_string == data.date_string;
-
-        delete oldIdMap[id];
+        data.hideDate = !!prev && prev.date_string == data.date_string;
       }
 
-      // Remove stale, previously rendered items from the DOM.
-      for (var id in oldIdMap) {
-        if (oldIdMap[id].parentNode)
-          oldIdMap[id].parentNode.removeChild(oldIdMap[id]);
-        delete oldIdMap[id];
-      }
-
-      for (var i = 0; i < this.items_.length; ++i) {
-        var item = this.items_[i];
-        if (item.parentNode)  // Already in the DOM; skip.
-          continue;
-
-        var before = null;
-        // Find the next rendered item after this one, and insert before it.
-        for (var j = i + 1; !before && j < this.items_.length; ++j) {
-          if (this.items_[j].parentNode)
-            before = this.items_[j];
-        }
-        // If |before| is null, |item| will just get added at the end.
-        this.$['downloads-list'].insertBefore(item, before);
-      }
+      // TODO(dbeam): this resets the scroll position, which is a huge bummer.
+      // Removing something from the bottom of the list should not scroll you
+      // back to the top. The grand plan is to restructure how the C++ sends the
+      // JS data so that it only gets updates (rather than the most recent set
+      // of items). TL;DR - we can't ship with this bug.
+      this.items_ = list;
 
       var hasDownloads = this.size_() > 0;
       if (!hasDownloads) {
@@ -15604,9 +16853,6 @@
         this.$.toolbar.downloadsShowing = this.hasDownloads_;
 
       this.$.panel.classList.remove('loading');
-
-      var allReady = this.items_.map(function(i) { return i.readyPromise; });
-      Promise.all(allReady).then(this.rebuildFocusGrid_.bind(this));
     },
 
     /**
@@ -15614,19 +16860,9 @@
      * @private
      */
     updateItem_: function(data) {
-      var item = this.idMap_[data.id];
-
-      var activeControl = this.shadowRoot.activeElement == item ?
-          item.shadowRoot.activeElement : null;
-
-      item.update(data);
-
-      this.async(function() {
-        if (activeControl && !cr.ui.FocusRow.isFocusable(activeControl)) {
-          var focusRow = this.focusGrid_.getRowForRoot(item.content);
-          focusRow.getEquivalentElement(activeControl).focus();
-        }
-      }.bind(this));
+      var index = this.idToIndex_[data.id];
+      this.set('items_.' + index, data);
+      this.$['downloads-list'].updateSizeForItem(index);
     },
   });
 
diff --git a/chrome/browser/resources/md_downloads/item.css b/chrome/browser/resources/md_downloads/item.css
index 4e668267..9a4576f 100644
--- a/chrome/browser/resources/md_downloads/item.css
+++ b/chrome/browser/resources/md_downloads/item.css
@@ -5,14 +5,13 @@
 :host {
   display: flex;
   flex-direction: column;
-  margin: 12px 0;
 }
 
 #date {
   color: #7c7c7c;
   font-size: 100%;
   font-weight: 500;
-  margin: 18px auto 16px;  /* This top margin + :host top margin = 30px. */
+  margin: 24px auto 10px;
   width: var(--downloads-item-width);
 }
 
@@ -21,7 +20,7 @@
   border-radius: 2px;
   display: flex;
   flex: none;
-  margin: 0 auto;
+  margin: 6px auto;
   min-height: 103px;
   position: relative;
   width: var(--downloads-item-width);
diff --git a/chrome/browser/resources/md_downloads/item.html b/chrome/browser/resources/md_downloads/item.html
index 56d7e1b..ffe95f5 100644
--- a/chrome/browser/resources/md_downloads/item.html
+++ b/chrome/browser/resources/md_downloads/item.html
@@ -8,7 +8,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
 <link rel="import" href="chrome://downloads/action_service.html">
 <link rel="import" href="chrome://downloads/constants.html">
-<link rel="import" href="chrome://downloads/throttled_icon_loader.html">
 
 <dom-module id="downloads-item">
   <template>
diff --git a/chrome/browser/resources/md_downloads/item.js b/chrome/browser/resources/md_downloads/item.js
index fb5ad71..3bce0bf9 100644
--- a/chrome/browser/resources/md_downloads/item.js
+++ b/chrome/browser/resources/md_downloads/item.js
@@ -6,14 +6,6 @@
   var Item = Polymer({
     is: 'downloads-item',
 
-    /**
-     * @param {!downloads.ThrottledIconLoader} iconLoader
-     */
-    factoryImpl: function(iconLoader) {
-      /** @private {!downloads.ThrottledIconLoader} */
-      this.iconLoader_ = iconLoader;
-    },
-
     properties: {
       data: {
         type: Object,
@@ -24,15 +16,6 @@
         value: true,
       },
 
-      readyPromise: {
-        type: Object,
-        value: function() {
-          return new Promise(function(resolve, reject) {
-            this.resolveReadyPromise_ = resolve;
-          }.bind(this));
-        },
-      },
-
       completelyOnDisk_: {
         computed: 'computeCompletelyOnDisk_(' +
             'data.state, data.file_externally_removed)',
@@ -105,21 +88,11 @@
       // TODO(dbeam): this gets called way more when I observe data.by_ext_id
       // and data.by_ext_name directly. Why?
       'observeControlledBy_(controlledBy_)',
+      'observeIsDangerous_(isDangerous_, data.file_path)',
     ],
 
     ready: function() {
       this.content = this.$.content;
-      this.resolveReadyPromise_();
-    },
-
-    /** @param {!downloads.Data} data */
-    update: function(data) {
-      this.data = data;
-
-      if (!this.isDangerous_) {
-        var icon = 'chrome://fileicon/' + encodeURIComponent(data.file_path);
-        this.iconLoader_.loadScaledIcon(this.$['file-icon'], icon);
-      }
     },
 
     /** @private */
@@ -280,6 +253,14 @@
     },
 
     /** @private */
+    observeIsDangerous_: function() {
+      if (this.data && !this.isDangerous_) {
+        var filePath = encodeURIComponent(this.data.file_path);
+        this.$['file-icon'].src = 'chrome://fileicon/' + filePath;
+      }
+    },
+
+    /** @private */
     onCancelTap_: function() {
       downloads.ActionService.getInstance().cancel(this.data.id);
     },
diff --git a/chrome/browser/resources/md_downloads/manager.html b/chrome/browser/resources/md_downloads/manager.html
index 46fb6ebc..628f0ea 100644
--- a/chrome/browser/resources/md_downloads/manager.html
+++ b/chrome/browser/resources/md_downloads/manager.html
@@ -5,6 +5,7 @@
 <link rel="import" href="chrome://resources/html/cr/ui/focus_grid.html">
 <link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-header-panel/paper-header-panel.html">
 <link rel="import" href="chrome://downloads/action_service.html">
 <link rel="import" href="chrome://downloads/constants.html">
@@ -16,8 +17,14 @@
   <template>
     <paper-header-panel id="panel" class="loading">
       <downloads-toolbar class="paper-header" id="toolbar"></downloads-toolbar>
-      <div id="downloads-list" hidden$="[[!hasDownloads_]]"></div>
-      <div id="no-downloads" hidden$="[[hasDownloads_]]">
+      <iron-list id="downloads-list" items="{{items_}}"
+          hidden="[[!hasDownloads_]]">
+        <template>
+          <downloads-item data="[[item]]" hide-date="[[item.hideDate]]">
+          </downloads-item>
+        </template>
+      </iron-list>
+      <div id="no-downloads" hidden="[[hasDownloads_]]">
         <div>
           <div class="illustration"></div>
           <span><!-- Text populated in Manager#updateAll_(). --></span>
diff --git a/chrome/browser/resources/md_downloads/manager.js b/chrome/browser/resources/md_downloads/manager.js
index 9c4c9ff..e4c54e4 100644
--- a/chrome/browser/resources/md_downloads/manager.js
+++ b/chrome/browser/resources/md_downloads/manager.js
@@ -11,15 +11,10 @@
         type: Boolean,
         value: false,
       },
-    },
 
-    /**
-     * @return {number} A guess at how many items could be visible at once.
-     * @private
-     */
-    guesstimateNumberOfVisibleItems_: function() {
-      var toolbarHeight = this.$.toolbar.offsetHeight;
-      return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1;
+      items_: {
+        type: Array,
+      },
     },
 
     /**
@@ -59,33 +54,6 @@
       downloads.ActionService.getInstance().search('');
     },
 
-    /** @private */
-    rebuildFocusGrid_: function() {
-      var activeElement = this.shadowRoot.activeElement;
-
-      var activeItem;
-      if (activeElement && activeElement.tagName == 'downloads-item')
-        activeItem = activeElement;
-
-      var activeControl = activeItem && activeItem.shadowRoot.activeElement;
-
-      /** @private {!cr.ui.FocusGrid} */
-      this.focusGrid_ = this.focusGrid_ || new cr.ui.FocusGrid;
-      this.focusGrid_.destroy();
-
-      var boundary = this.$['downloads-list'];
-
-      this.items_.forEach(function(item) {
-        var focusRow = new downloads.FocusRow(item.content, boundary);
-        this.focusGrid_.addRow(focusRow);
-
-        if (item == activeItem && !cr.ui.FocusRow.isFocusable(activeControl))
-          focusRow.getEquivalentElement(activeControl).focus();
-      }, this);
-
-      this.focusGrid_.ensureRowActive();
-    },
-
     /**
      * @return {number} The number of downloads shown on the page.
      * @private
@@ -100,62 +68,24 @@
      * @private
      */
     updateAll_: function(list) {
-      var oldIdMap = this.idMap_ || {};
-
-      /** @private {!Object<!downloads.Item>} */
-      this.idMap_ = {};
-
-      /** @private {!Array<!downloads.Item>} */
-      this.items_ = [];
-
-      if (!this.iconLoader_) {
-        var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1);
-        /** @private {downloads.ThrottledIconLoader} */
-        this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate);
-      }
+      /** @private {!Object<number>} */
+      this.idToIndex_ = {};
 
       for (var i = 0; i < list.length; ++i) {
         var data = list[i];
-        var id = data.id;
 
-        // Re-use old items when possible (saves work, preserves focus).
-        var item = oldIdMap[id] || new downloads.Item(this.iconLoader_);
+        this.idToIndex_[data.id] = data.index = i;
 
-        this.idMap_[id] = item;  // Associated by ID for fast lookup.
-        this.items_.push(item);  // Add to sorted list for order.
-
-        // Render |item| but don't actually add to the DOM yet. |this.items_|
-        // must be fully created to be able to find the right spot to insert.
-        item.update(data);
-
-        // Collapse redundant dates.
         var prev = list[i - 1];
-        item.hideDate = !!prev && prev.date_string == data.date_string;
-
-        delete oldIdMap[id];
+        data.hideDate = !!prev && prev.date_string == data.date_string;
       }
 
-      // Remove stale, previously rendered items from the DOM.
-      for (var id in oldIdMap) {
-        if (oldIdMap[id].parentNode)
-          oldIdMap[id].parentNode.removeChild(oldIdMap[id]);
-        delete oldIdMap[id];
-      }
-
-      for (var i = 0; i < this.items_.length; ++i) {
-        var item = this.items_[i];
-        if (item.parentNode)  // Already in the DOM; skip.
-          continue;
-
-        var before = null;
-        // Find the next rendered item after this one, and insert before it.
-        for (var j = i + 1; !before && j < this.items_.length; ++j) {
-          if (this.items_[j].parentNode)
-            before = this.items_[j];
-        }
-        // If |before| is null, |item| will just get added at the end.
-        this.$['downloads-list'].insertBefore(item, before);
-      }
+      // TODO(dbeam): this resets the scroll position, which is a huge bummer.
+      // Removing something from the bottom of the list should not scroll you
+      // back to the top. The grand plan is to restructure how the C++ sends the
+      // JS data so that it only gets updates (rather than the most recent set
+      // of items). TL;DR - we can't ship with this bug.
+      this.items_ = list;
 
       var hasDownloads = this.size_() > 0;
       if (!hasDownloads) {
@@ -170,9 +100,6 @@
         this.$.toolbar.downloadsShowing = this.hasDownloads_;
 
       this.$.panel.classList.remove('loading');
-
-      var allReady = this.items_.map(function(i) { return i.readyPromise; });
-      Promise.all(allReady).then(this.rebuildFocusGrid_.bind(this));
     },
 
     /**
@@ -180,19 +107,9 @@
      * @private
      */
     updateItem_: function(data) {
-      var item = this.idMap_[data.id];
-
-      var activeControl = this.shadowRoot.activeElement == item ?
-          item.shadowRoot.activeElement : null;
-
-      item.update(data);
-
-      this.async(function() {
-        if (activeControl && !cr.ui.FocusRow.isFocusable(activeControl)) {
-          var focusRow = this.focusGrid_.getRowForRoot(item.content);
-          focusRow.getEquivalentElement(activeControl).focus();
-        }
-      }.bind(this));
+      var index = this.idToIndex_[data.id];
+      this.set('items_.' + index, data);
+      this.$['downloads-list'].updateSizeForItem(index);
     },
   });
 
diff --git a/chrome/browser/resources/md_downloads/vulcanized.html b/chrome/browser/resources/md_downloads/vulcanized.html
index 37e4737..fff21ea 100644
--- a/chrome/browser/resources/md_downloads/vulcanized.html
+++ b/chrome/browser/resources/md_downloads/vulcanized.html
@@ -1632,7 +1632,46 @@
   </style>
 <script src="chrome://downloads/strings.js"></script>
 </head>
-<body><div hidden="" by-vulcanize=""><dom-module id="paper-header-panel" assetpath="chrome://resources/polymer/v1_0/paper-header-panel/">
+<body><div hidden="" by-vulcanize=""><dom-module id="iron-list" assetpath="chrome://resources/polymer/v1_0/iron-list/">
+  <template>
+    <style>
+      :host {
+        display: block;
+      }
+  
+      :host(.has-scroller) {
+        overflow: auto;
+      }
+  
+      :host(:not(.has-scroller)) {
+        position: relative;
+      }
+  
+      #items {
+        @apply(--iron-list-items-container);
+        position: relative;
+      }
+  
+      #items > ::content > * {
+        width: 100%;
+        box-sizing: border-box;
+        position: absolute;
+        top: 0;
+        will-change: transform;
+      }
+    </style>
+
+    <array-selector id="selector" items="{{items}}" selected="{{selectedItems}}" selected-item="{{selectedItem}}">
+    </array-selector>
+
+    <div id="items">
+      <content></content>
+    </div>
+
+  </template>
+</dom-module>
+
+<dom-module id="paper-header-panel" assetpath="chrome://resources/polymer/v1_0/paper-header-panel/">
 
   <style>
 /**
@@ -1770,674 +1809,6 @@
 
 </dom-module>
 
-<dom-module id="paper-material" assetpath="chrome://resources/polymer/v1_0/paper-material/">
-  <style>
-    :host {
-      display: block;
-      position: relative;
-    }
-
-    :host([animated]) {
-      @apply(--shadow-transition);
-    }
-
-    :host([elevation="1"]) {
-      @apply(--shadow-elevation-2dp);
-    }
-
-    :host([elevation="2"]) {
-      @apply(--shadow-elevation-4dp);
-    }
-
-    :host([elevation="3"]) {
-      @apply(--shadow-elevation-6dp);
-    }
-
-    :host([elevation="4"]) {
-      @apply(--shadow-elevation-8dp);
-    }
-
-    :host([elevation="5"]) {
-      @apply(--shadow-elevation-16dp);
-    }
-  </style>
-  <template>
-    <content></content>
-  </template>
-</dom-module>
-<dom-module id="paper-ripple" assetpath="chrome://resources/polymer/v1_0/paper-ripple/">
-
-  
-  
-  <template>
-    <style>
-      :host {
-        display: block;
-        position: absolute;
-        border-radius: inherit;
-        overflow: hidden;
-        top: 0;
-        left: 0;
-        right: 0;
-        bottom: 0;
-      }
-  
-      :host([animating]) {
-        /* This resolves a rendering issue in Chrome (as of 40) where the
-           ripple is not properly clipped by its parent (which may have
-           rounded corners). See: http://jsbin.com/temexa/4
-  
-           Note: We only apply this style conditionally. Otherwise, the browser
-           will create a new compositing layer for every ripple element on the
-           page, and that would be bad. */
-        -webkit-transform: translate(0, 0);
-        transform: translate3d(0, 0, 0);
-      }
-  
-      :host([noink]) {
-        pointer-events: none;
-      }
-  
-      #background,
-      #waves,
-      .wave-container,
-      .wave {
-        pointer-events: none;
-        position: absolute;
-        top: 0;
-        left: 0;
-        width: 100%;
-        height: 100%;
-      }
-  
-      #background,
-      .wave {
-        opacity: 0;
-      }
-  
-      #waves,
-      .wave {
-        overflow: hidden;
-      }
-  
-      .wave-container,
-      .wave {
-        border-radius: 50%;
-      }
-  
-      :host(.circle) #background,
-      :host(.circle) #waves {
-        border-radius: 50%;
-      }
-  
-      :host(.circle) .wave-container {
-        overflow: hidden;
-      }
-    </style>
-    
-    <div id="background"></div>
-    <div id="waves"></div>
-  </template>
-</dom-module>
-<dom-module id="paper-button" assetpath="chrome://resources/polymer/v1_0/paper-button/">
-  <template>
-
-    <style>
-      :host {
-        display: inline-block;
-        position: relative;
-        box-sizing: border-box;
-        min-width: 5.14em;
-        margin: 0 0.29em;
-        background: transparent;
-        text-align: center;
-        font: inherit;
-        text-transform: uppercase;
-        outline-width: 0;
-        border-radius: 3px;
-        -moz-user-select: none;
-        -ms-user-select: none;
-        -webkit-user-select: none;
-        user-select: none;
-        cursor: pointer;
-        z-index: 0;
-        padding: 0.7em 0.57em;
-
-        @apply(--paper-button);
-      }
-
-      :host([raised]) .keyboard-focus {
-        font-weight: bold;
-        @apply(--paper-button-raised-keyboard-focus);
-      }
-
-      :host(:not([raised])) .keyboard-focus {
-        font-weight: bold;
-        @apply(--paper-button-flat-keyboard-focus);
-      }
-
-      :host([disabled]) {
-        background: #eaeaea;
-        color: #a8a8a8;
-        cursor: auto;
-        pointer-events: none;
-
-        @apply(--paper-button-disabled);
-      }
-
-      :host([noink]) paper-ripple {
-        display: none;
-      }
-
-      paper-ripple {
-        color: var(--paper-button-ink-color);
-      }
-
-      paper-material {
-        border-radius: inherit;
-        @apply(--layout-fit);
-      }
-
-      .content > ::content * {
-        text-transform: inherit;
-      }
-    </style>
-
-    <paper-ripple></paper-ripple>
-
-    <paper-material elevation="[[_elevation]]" animated=""></paper-material>
-
-    <div class$="[[_computeContentClass(receivedFocusFromKeyboard)]]">
-      <content></content>
-    </div>
-
-  </template>
-</dom-module>
-
-<dom-module id="paper-progress" assetpath="chrome://resources/polymer/v1_0/paper-progress/">
-  <style>
-    :host {
-      display: block;
-      width: 200px;
-      position: relative;
-      overflow: hidden;
-    }
-
-    #progressContainer {
-      position: relative;
-    }
-
-    #progressContainer,
-    /* the stripe for the indeterminate animation*/
-    .indeterminate:after {
-      height: var(--paper-progress-height, 4px);
-    }
-
-    #primaryProgress,
-    #secondaryProgress,
-    .indeterminate:after {
-      @apply(--layout-fit);
-    }
-
-    #progressContainer,
-    .indeterminate:after {
-      background-color: var(--paper-progress-container-color, --google-grey-300);
-    }
-
-    :host(.transiting) #primaryProgress,
-    :host(.transiting) #secondaryProgress {
-      -webkit-transition-property: -webkit-transform;
-      transition-property: transform;
-
-      /* Duration */
-      -webkit-transition-duration: var(--paper-progress-transition-duration, 0.08s);
-      transition-duration: var(--paper-progress-transition-duration, 0.08s);
-
-      /* Timing function */
-      -webkit-transition-timing-function: var(--paper-progress-transition-timing-function, ease);
-      transition-timing-function: var(--paper-progress-transition-timing-function, ease);
-
-      /* Delay */
-      -webkit-transition-delay: var(--paper-progress-transition-delay, 0s);
-      transition-delay: var(--paper-progress-transition-delay, 0s);
-    }
-
-    #primaryProgress,
-    #secondaryProgress {
-      @apply(--layout-fit);
-      -webkit-transform-origin: left center;
-      transform-origin: left center;
-      -webkit-transform: scaleX(0);
-      transform: scaleX(0);
-      will-change: transform;
-    }
-
-    #primaryProgress {
-      background-color: var(--paper-progress-active-color, --google-green-500);
-    }
-
-    #secondaryProgress {
-      position: relative;
-      background-color: var(--paper-progress-secondary-color, --google-green-100);
-    }
-
-    :host([disabled]) #primaryProgress {
-      background-color: var(--paper-progress-disabled-active-color, --google-grey-500);
-    }
-
-    :host([disabled]) #secondaryProgress {
-      background-color: var(--paper-progress-disabled-active-color, --google-grey-300);
-    }
-
-    :host(:not([disabled])) #primaryProgress.indeterminate {
-      -webkit-transform-origin: right center;
-      transform-origin: right center;
-      -webkit-animation: indeterminate-bar 2s linear infinite;
-      animation: indeterminate-bar 2s linear infinite;
-    }
-
-    :host(:not([disabled])) #primaryProgress.indeterminate:after {
-      content: "";
-      -webkit-transform-origin: center center;
-      transform-origin: center center;
-
-      -webkit-animation: indeterminate-splitter 2s linear infinite;
-      animation: indeterminate-splitter 2s linear infinite;
-    }
-
-    @-webkit-keyframes indeterminate-bar {
-      0% {
-        -webkit-transform: scaleX(1) translateX(-100%);
-      }
-      50% {
-        -webkit-transform: scaleX(1) translateX(0%);
-      }
-      75% {
-        -webkit-transform: scaleX(1) translateX(0%);
-        -webkit-animation-timing-function: cubic-bezier(.28,.62,.37,.91);
-      }
-      100% {
-        -webkit-transform: scaleX(0) translateX(0%);
-      }
-    }
-
-    @-webkit-keyframes indeterminate-splitter {
-      0% {
-        -webkit-transform: scaleX(.75) translateX(-125%);
-      }
-      30% {
-        -webkit-transform: scaleX(.75) translateX(-125%);
-        -webkit-animation-timing-function: cubic-bezier(.42,0,.6,.8);
-      }
-      90% {
-        -webkit-transform: scaleX(.75) translateX(125%);
-      }
-      100% {
-        -webkit-transform: scaleX(.75) translateX(125%);
-      }
-    }
-
-    @keyframes indeterminate-bar {
-      0% {
-        transform: scaleX(1) translateX(-100%);
-      }
-      50% {
-        transform: scaleX(1) translateX(0%);
-      }
-      75% {
-        transform: scaleX(1) translateX(0%);
-        animation-timing-function: cubic-bezier(.28,.62,.37,.91);
-      }
-      100% {
-        transform: scaleX(0) translateX(0%);
-      }
-    }
-
-    @keyframes indeterminate-splitter {
-      0% {
-        transform: scaleX(.75) translateX(-125%);
-      }
-      30% {
-        transform: scaleX(.75) translateX(-125%);
-        animation-timing-function: cubic-bezier(.42,0,.6,.8);
-      }
-      90% {
-        transform: scaleX(.75) translateX(125%);
-      }
-      100% {
-        transform: scaleX(.75) translateX(125%);
-      }
-    }
-  </style>
-  <template>
-    <div id="progressContainer">
-      <div id="secondaryProgress" hidden$="[[_hideSecondaryProgress(secondaryRatio)]]"></div>
-      <div id="primaryProgress"></div>
-    </div>
-  </template>
-</dom-module>
-
-<dom-module id="downloads-item" assetpath="chrome://downloads/">
-  <template>
-    <template is="dom-if" if="[[!hideDate]]">
-      <h3 id="date">[[computeDate_(data.since_string, data.date_string)]]</h3>
-    </template>
-
-    <div id="content" on-dragstart="onDragStart_" class$="[[computeClass_(isActive_, isDangerous_, showProgress_)]]">
-      <div id="file-icon-wrapper" class="icon-wrapper">
-        <img class="icon" id="file-icon" alt="" hidden="[[isDangerous_]]">
-        <div class="icon" id="warning" hidden="[[!isDangerous_]]"></div>
-      </div>
-
-      <div id="details">
-        <div id="title-area"><a is="action-link" id="file-link" href="[[data.url]]" on-tap="onFileLinkTap_" hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><span id="name" hidden="[[completelyOnDisk_]]">[[data.file_name]]</span>
-          <span id="tag">[[computeTag_(data.state, data.last_reason_text, data.file_externally_removed)]]</span>
-        </div>
-
-        <a id="url" target="_blank" href="[[data.url]]">[[data.url]]</a>
-
-        <div id="description">[[computeDescription_(data.state, data.danger_type, data.file_name, data.progress_status_text)]]</div>
-
-        <template is="dom-if" if="[[showProgress_]]">
-          <paper-progress id="progress" indeterminate="[[isIndeterminate_(data.percent)]]" value="[[data.percent]]"></paper-progress>
-        </template>
-
-        <div id="safe" class="controls" hidden="[[isDangerous_]]">
-          <a is="action-link" id="show" i18n-content="controlShowInFolder" on-tap="onShowTap_" hidden="[[!completelyOnDisk_]]"></a>
-          <template is="dom-if" if="[[data.retry]]">
-            <paper-button id="retry" on-tap="onRetryTap_">[[i18n_.retry]]</paper-button>
-          </template>
-          <template is="dom-if" if="[[isInProgress_]]">
-            <paper-button id="pause" on-tap="onPauseTap_">[[i18n_.pause]]</paper-button>
-          </template>
-          <template is="dom-if" if="[[data.resume]]">
-            <paper-button id="resume" on-tap="onResumeTap_">[[i18n_.resume]]</paper-button>
-          </template>
-          <template is="dom-if" if="[[showCancel_]]">
-            <paper-button id="cancel" on-tap="onCancelTap_">[[i18n_.cancel]]</paper-button>
-          </template>
-          <span id="controlled-by"></span>
-        </div>
-
-        <template is="dom-if" if="[[isDangerous_]]">
-          <div id="dangerous" class="controls">
-            
-            <template is="dom-if" if="[[!isMalware_]]">
-              <paper-button id="discard" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.discard]]</paper-button>
-              <paper-button id="save" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.save]]</paper-button>
-            </template>
-
-            
-            <template is="dom-if" if="[[isMalware_]]">
-              <paper-button id="danger-remove" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.remove]]</paper-button>
-              <paper-button id="restore" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.restore]</paper-button>
-            </template>
-          </div>
-        </template>
-      </div>
-
-      <div id="remove-wrapper" class="icon-wrapper">
-        <paper-icon-button id="remove" icon="clear" i18n-values="title:controlRemoveFromList" style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]" on-tap="onRemoveTap_"></paper-icon-button>
-      </div>
-
-      <div id="incognito" i18n-values="title:inIncognito" hidden="[[!data.otr]]"></div>
-    </div>
-
-  </template>
-  <style>
-/* 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. */
-
-[is='action-link'] {
-  cursor: pointer;
-  display: inline-block;
-  text-decoration: none;
-}
-
-[is='action-link']:hover {
-  text-decoration: underline;
-}
-
-[is='action-link']:active {
-  color: rgb(5, 37, 119);
-  text-decoration: underline;
-}
-
-[is='action-link'][disabled] {
-  color: #999;
-  cursor: default;
-  pointer-events: none;
-  text-decoration: none;
-}
-
-[is='action-link'].no-outline {
-  outline: none;
-}
-
-</style>
-  <style>
-/* 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. */
-
-* {
-  --downloads-item-width: 622px;
-}
-
-paper-button {
-  font-weight: 500;
-  margin: 0;
-  min-width: auto;
-}
-
-</style>
-  <style>
-/* 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. */
-
-:host {
-  display: flex;
-  flex-direction: column;
-  margin: 12px 0;
-}
-
-#date {
-  color: #7c7c7c;
-  font-size: 100%;
-  font-weight: 500;
-  margin: 18px auto 16px;  /* This top margin + :host top margin = 30px. */
-  width: var(--downloads-item-width);
-}
-
-#content {
-  background: white;
-  border-radius: 2px;
-  display: flex;
-  flex: none;
-  margin: 0 auto;
-  min-height: 103px;
-  position: relative;
-  width: var(--downloads-item-width);
-}
-
-#content.is-active {
-  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .05), 0 1px 4px 0 rgba(0, 0, 0, .08),
-              0 3px 1px -2px rgba(0, 0, 0, .2);
-}
-
-#content:not(.is-active) {
-  opacity: .6;
-}
-
-#details {
-  -webkit-border-start: 1px #d8d8d8 solid;
-  -webkit-padding-end: 16px;
-  -webkit-padding-start: 24px;
-  display: flex;
-  flex: 1;
-  flex-direction: column;
-  padding-bottom: 12px;
-  padding-top: 16px;
-}
-
-#content:not(.is-active) #details {
-  color: #bababa;
-}
-
-#content:not(.is-active) #name {
-  text-decoration: line-through;
-}
-
-.icon-wrapper {
-  align-self: center;
-  flex: none;
-  justify-content: center;
-  margin: 0 24px;
-}
-
-.icon {
-  height: 32px;
-  width: 32px;
-}
-
-#content:-webkit-any(.show-progress, .dangerous) #file-icon-wrapper {
-  /* TODO(dbeam): animate from top-aligned to centered when items finish? */
-  align-self: flex-start;
-  padding-top: 16px;
-}
-
-#content:not(.is-active) .icon {
-  -webkit-filter: grayscale(100%);
-  opacity: .5;
-}
-
-#warning {
-  -webkit-mask-image: url("chrome://resources/images/warning.svg");
-  -webkit-mask-repeat: no-repeat;
-  -webkit-mask-size: 100%;
-  background: rgb(255, 193, 7);
-}
-
-#name,
-#file-link,
-#url {
-  max-width: 100%;
-}
-
-#name,
-#file-link {
-  font-weight: 500;
-  word-break: break-all;
-}
-
-#name {
-  -webkit-margin-end: 12px;  /* Only really affects #tag. */
-}
-
-.is-active :-webkit-any(#name, #file-link, #pause, #resume, #show) {
-  color: rgb(51, 103, 214);
-}
-
-#tag {
-  color: #5a5a5a;
-  font-weight: 500;
-}
-
-#url {
-  color: inherit;
-  margin-top: 6px;
-  overflow: hidden;
-  text-decoration: none;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-
-.is-active #url {
-  color: #969696;
-}
-
-#progress,
-#description:not(:empty),
-.controls {
-  margin-top: 16px;
-}
-
-.is-active #description {
-  color: #616161;
-}
-
-.dangerous #description {
-  color: rgb(239, 108, 0);
-}
-
-#progress {
-  --paper-progress-active-color: rgb(54, 126, 237);
-  --paper-progress-container-color: rgb(223, 222, 223);
-  width: auto;
-}
-
-.controls {
-  -webkit-margin-start: -.57em;
-}
-
-#cancel,
-#retry,
-.keep,
-.discard {
-  color: #5a5a5a;
-}
-
-#show {
-  margin: .7em .57em;
-}
-
-#controlled-by {
-  -webkit-margin-start: 8px;
-}
-
-#controlled-by,
-#controlled-by a {
-  color: #5a5a5a;
-}
-
-.is-active #controlled-by {
-  color: #333;
-}
-
-.is-active #controlled-by a {
-  color: rgb(51, 103, 214);
-}
-
-#remove-wrapper {
-  align-self: flex-start;
-  margin: 0;
-}
-
-#remove {
-  --iron-icon-height: 16px;
-  --iron-icon-width: 16px;
-  --layout-inline: {
-    /* HACK(dbeam): we probably shouldn't be overriding Polymer like this. */
-  };
-  color: #969696;
-  height: 16px;
-  padding: 8px;
-  width: 16px;
-}
-
-#incognito {
-  bottom: 20px;
-  content: -webkit-image-set(
-      url("chrome://downloads/1x/incognito_marker.png") 1x,
-      url("chrome://downloads/2x/incognito_marker.png") 2x);
-  position: absolute;
-  right: 10px;
-}
-
-</style>
-  </dom-module>
 <dom-module id="iron-icon" assetpath="chrome://resources/polymer/v1_0/iron-icon/">
 
   <style>
@@ -2727,6 +2098,662 @@
 <g id="zoom-out"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"></path></g>
 </defs></svg>
 </iron-iconset-svg>
+<dom-module id="paper-material" assetpath="chrome://resources/polymer/v1_0/paper-material/">
+  <style>
+    :host {
+      display: block;
+      position: relative;
+    }
+
+    :host([animated]) {
+      @apply(--shadow-transition);
+    }
+
+    :host([elevation="1"]) {
+      @apply(--shadow-elevation-2dp);
+    }
+
+    :host([elevation="2"]) {
+      @apply(--shadow-elevation-4dp);
+    }
+
+    :host([elevation="3"]) {
+      @apply(--shadow-elevation-6dp);
+    }
+
+    :host([elevation="4"]) {
+      @apply(--shadow-elevation-8dp);
+    }
+
+    :host([elevation="5"]) {
+      @apply(--shadow-elevation-16dp);
+    }
+  </style>
+  <template>
+    <content></content>
+  </template>
+</dom-module>
+<dom-module id="paper-ripple" assetpath="chrome://resources/polymer/v1_0/paper-ripple/">
+
+  
+  
+  <template>
+    <style>
+      :host {
+        display: block;
+        position: absolute;
+        border-radius: inherit;
+        overflow: hidden;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+      }
+  
+      :host([animating]) {
+        /* This resolves a rendering issue in Chrome (as of 40) where the
+           ripple is not properly clipped by its parent (which may have
+           rounded corners). See: http://jsbin.com/temexa/4
+  
+           Note: We only apply this style conditionally. Otherwise, the browser
+           will create a new compositing layer for every ripple element on the
+           page, and that would be bad. */
+        -webkit-transform: translate(0, 0);
+        transform: translate3d(0, 0, 0);
+      }
+  
+      :host([noink]) {
+        pointer-events: none;
+      }
+  
+      #background,
+      #waves,
+      .wave-container,
+      .wave {
+        pointer-events: none;
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+      }
+  
+      #background,
+      .wave {
+        opacity: 0;
+      }
+  
+      #waves,
+      .wave {
+        overflow: hidden;
+      }
+  
+      .wave-container,
+      .wave {
+        border-radius: 50%;
+      }
+  
+      :host(.circle) #background,
+      :host(.circle) #waves {
+        border-radius: 50%;
+      }
+  
+      :host(.circle) .wave-container {
+        overflow: hidden;
+      }
+    </style>
+    
+    <div id="background"></div>
+    <div id="waves"></div>
+  </template>
+</dom-module>
+<dom-module id="paper-button" assetpath="chrome://resources/polymer/v1_0/paper-button/">
+  <template strip-whitespace="">
+
+    <style include="paper-material">
+      :host {
+        display: inline-block;
+        position: relative;
+        box-sizing: border-box;
+        min-width: 5.14em;
+        margin: 0 0.29em;
+        background: transparent;
+        text-align: center;
+        font: inherit;
+        text-transform: uppercase;
+        outline-width: 0;
+        border-radius: 3px;
+        -moz-user-select: none;
+        -ms-user-select: none;
+        -webkit-user-select: none;
+        user-select: none;
+        cursor: pointer;
+        z-index: 0;
+        padding: 0.7em 0.57em;
+
+        @apply(--paper-button);
+      }
+
+      :host([raised].keyboard-focus) {
+        font-weight: bold;
+        @apply(--paper-button-raised-keyboard-focus);
+      }
+
+      :host(:not([raised]).keyboard-focus) {
+        font-weight: bold;
+        @apply(--paper-button-flat-keyboard-focus);
+      }
+
+      :host([disabled]) {
+        background: #eaeaea;
+        color: #a8a8a8;
+        cursor: auto;
+        pointer-events: none;
+
+        @apply(--paper-button-disabled);
+      }
+
+      paper-ripple {
+        color: var(--paper-button-ink-color);
+      }
+
+      :host > ::content * {
+        text-transform: inherit;
+      }
+    </style>
+    <content></content>
+  </template>
+</dom-module>
+
+<dom-module id="paper-progress" assetpath="chrome://resources/polymer/v1_0/paper-progress/">
+  <style>
+    :host {
+      display: block;
+      width: 200px;
+      position: relative;
+      overflow: hidden;
+    }
+
+    #progressContainer {
+      position: relative;
+    }
+
+    #progressContainer,
+    /* the stripe for the indeterminate animation*/
+    .indeterminate:after {
+      height: var(--paper-progress-height, 4px);
+    }
+
+    #primaryProgress,
+    #secondaryProgress,
+    .indeterminate:after {
+      @apply(--layout-fit);
+    }
+
+    #progressContainer,
+    .indeterminate:after {
+      background-color: var(--paper-progress-container-color, --google-grey-300);
+    }
+
+    :host(.transiting) #primaryProgress,
+    :host(.transiting) #secondaryProgress {
+      -webkit-transition-property: -webkit-transform;
+      transition-property: transform;
+
+      /* Duration */
+      -webkit-transition-duration: var(--paper-progress-transition-duration, 0.08s);
+      transition-duration: var(--paper-progress-transition-duration, 0.08s);
+
+      /* Timing function */
+      -webkit-transition-timing-function: var(--paper-progress-transition-timing-function, ease);
+      transition-timing-function: var(--paper-progress-transition-timing-function, ease);
+
+      /* Delay */
+      -webkit-transition-delay: var(--paper-progress-transition-delay, 0s);
+      transition-delay: var(--paper-progress-transition-delay, 0s);
+    }
+
+    #primaryProgress,
+    #secondaryProgress {
+      @apply(--layout-fit);
+      -webkit-transform-origin: left center;
+      transform-origin: left center;
+      -webkit-transform: scaleX(0);
+      transform: scaleX(0);
+      will-change: transform;
+    }
+
+    #primaryProgress {
+      background-color: var(--paper-progress-active-color, --google-green-500);
+    }
+
+    #secondaryProgress {
+      position: relative;
+      background-color: var(--paper-progress-secondary-color, --google-green-100);
+    }
+
+    :host([disabled]) #primaryProgress {
+      background-color: var(--paper-progress-disabled-active-color, --google-grey-500);
+    }
+
+    :host([disabled]) #secondaryProgress {
+      background-color: var(--paper-progress-disabled-active-color, --google-grey-300);
+    }
+
+    :host(:not([disabled])) #primaryProgress.indeterminate {
+      -webkit-transform-origin: right center;
+      transform-origin: right center;
+      -webkit-animation: indeterminate-bar 2s linear infinite;
+      animation: indeterminate-bar 2s linear infinite;
+    }
+
+    :host(:not([disabled])) #primaryProgress.indeterminate:after {
+      content: "";
+      -webkit-transform-origin: center center;
+      transform-origin: center center;
+
+      -webkit-animation: indeterminate-splitter 2s linear infinite;
+      animation: indeterminate-splitter 2s linear infinite;
+    }
+
+    @-webkit-keyframes indeterminate-bar {
+      0% {
+        -webkit-transform: scaleX(1) translateX(-100%);
+      }
+      50% {
+        -webkit-transform: scaleX(1) translateX(0%);
+      }
+      75% {
+        -webkit-transform: scaleX(1) translateX(0%);
+        -webkit-animation-timing-function: cubic-bezier(.28,.62,.37,.91);
+      }
+      100% {
+        -webkit-transform: scaleX(0) translateX(0%);
+      }
+    }
+
+    @-webkit-keyframes indeterminate-splitter {
+      0% {
+        -webkit-transform: scaleX(.75) translateX(-125%);
+      }
+      30% {
+        -webkit-transform: scaleX(.75) translateX(-125%);
+        -webkit-animation-timing-function: cubic-bezier(.42,0,.6,.8);
+      }
+      90% {
+        -webkit-transform: scaleX(.75) translateX(125%);
+      }
+      100% {
+        -webkit-transform: scaleX(.75) translateX(125%);
+      }
+    }
+
+    @keyframes indeterminate-bar {
+      0% {
+        transform: scaleX(1) translateX(-100%);
+      }
+      50% {
+        transform: scaleX(1) translateX(0%);
+      }
+      75% {
+        transform: scaleX(1) translateX(0%);
+        animation-timing-function: cubic-bezier(.28,.62,.37,.91);
+      }
+      100% {
+        transform: scaleX(0) translateX(0%);
+      }
+    }
+
+    @keyframes indeterminate-splitter {
+      0% {
+        transform: scaleX(.75) translateX(-125%);
+      }
+      30% {
+        transform: scaleX(.75) translateX(-125%);
+        animation-timing-function: cubic-bezier(.42,0,.6,.8);
+      }
+      90% {
+        transform: scaleX(.75) translateX(125%);
+      }
+      100% {
+        transform: scaleX(.75) translateX(125%);
+      }
+    }
+  </style>
+  <template>
+    <div id="progressContainer">
+      <div id="secondaryProgress" hidden$="[[_hideSecondaryProgress(secondaryRatio)]]"></div>
+      <div id="primaryProgress"></div>
+    </div>
+  </template>
+</dom-module>
+
+<dom-module id="downloads-item" assetpath="chrome://downloads/">
+  <template>
+    <template is="dom-if" if="[[!hideDate]]">
+      <h3 id="date">[[computeDate_(hideDate, data.since_string, data.date_string)]]</h3>
+    </template>
+
+    <div id="content" on-dragstart="onDragStart_" class$="[[computeClass_(isActive_, isDangerous_, showProgress_)]]">
+      <div id="file-icon-wrapper" class="icon-wrapper">
+        <img class="icon" id="file-icon" alt="" hidden="[[isDangerous_]]">
+        <iron-icon id="danger-icon" icon$="[[computeDangerIcon_(isDangerous_, data.danger_type)]]" hidden="[[!isDangerous_]]"></iron-icon>
+      </div>
+
+      <div id="details">
+        <div id="title-area"><a is="action-link" id="file-link" href="[[data.url]]" on-tap="onFileLinkTap_" hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><span id="name" hidden="[[completelyOnDisk_]]">[[data.file_name]]</span>
+          <span id="tag">[[computeTag_(data.state, data.last_reason_text, data.file_externally_removed)]]</span>
+        </div>
+
+        <a id="url" target="_blank" href="[[data.url]]">[[data.url]]</a>
+
+        <div id="description">[[computeDescription_(data.state, data.danger_type, data.file_name, data.progress_status_text)]]</div>
+
+        <template is="dom-if" if="[[showProgress_]]">
+          <paper-progress id="progress" indeterminate="[[isIndeterminate_(data.percent)]]" value="[[data.percent]]"></paper-progress>
+        </template>
+
+        <div id="safe" class="controls" hidden="[[isDangerous_]]">
+          <a is="action-link" id="show" i18n-content="controlShowInFolder" on-tap="onShowTap_" hidden="[[!completelyOnDisk_]]"></a>
+          <template is="dom-if" if="[[data.retry]]">
+            <paper-button id="retry" on-tap="onRetryTap_">[[i18n_.retry]]</paper-button>
+          </template>
+          <template is="dom-if" if="[[isInProgress_]]">
+            <paper-button id="pause" on-tap="onPauseTap_">[[i18n_.pause]]</paper-button>
+          </template>
+          <template is="dom-if" if="[[data.resume]]">
+            <paper-button id="resume" on-tap="onResumeTap_">[[i18n_.resume]]</paper-button>
+          </template>
+          <template is="dom-if" if="[[showCancel_]]">
+            <paper-button id="cancel" on-tap="onCancelTap_">[[i18n_.cancel]]</paper-button>
+          </template>
+          <span id="controlled-by"></span>
+        </div>
+
+        <template is="dom-if" if="[[isDangerous_]]">
+          <div id="dangerous" class="controls">
+            
+            <template is="dom-if" if="[[!isMalware_]]">
+              <paper-button id="discard" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.discard]]</paper-button>
+              <paper-button id="save" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.save]]</paper-button>
+            </template>
+
+            
+            <template is="dom-if" if="[[isMalware_]]">
+              <paper-button id="danger-remove" on-tap="onDiscardDangerousTap_" class="discard">[[i18n_.remove]]</paper-button>
+              <paper-button id="restore" on-tap="onSaveDangerousTap_" class="keep">[[i18n_.restore]</paper-button>
+            </template>
+          </div>
+        </template>
+      </div>
+
+      <div id="remove-wrapper" class="icon-wrapper">
+        <paper-icon-button id="remove" icon="clear" i18n-values="title:controlRemoveFromList" style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]" on-tap="onRemoveTap_"></paper-icon-button>
+      </div>
+
+      <div id="incognito" i18n-values="title:inIncognito" hidden="[[!data.otr]]"></div>
+    </div>
+
+  </template>
+  <style>
+/* 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. */
+
+[is='action-link'] {
+  cursor: pointer;
+  display: inline-block;
+  text-decoration: none;
+}
+
+[is='action-link']:hover {
+  text-decoration: underline;
+}
+
+[is='action-link']:active {
+  color: rgb(5, 37, 119);
+  text-decoration: underline;
+}
+
+[is='action-link'][disabled] {
+  color: #999;
+  cursor: default;
+  pointer-events: none;
+  text-decoration: none;
+}
+
+[is='action-link'].no-outline {
+  outline: none;
+}
+
+</style>
+  <style>
+/* 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. */
+
+* {
+  --downloads-item-width: 622px;
+}
+
+paper-button {
+  font-weight: 500;
+  margin: 0;
+  min-width: auto;
+}
+
+</style>
+  <style>
+/* 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. */
+
+:host {
+  display: flex;
+  flex-direction: column;
+}
+
+#date {
+  color: #7c7c7c;
+  font-size: 100%;
+  font-weight: 500;
+  margin: 24px auto 10px;
+  width: var(--downloads-item-width);
+}
+
+#content {
+  background: white;
+  border-radius: 2px;
+  display: flex;
+  flex: none;
+  margin: 6px auto;
+  min-height: 103px;
+  position: relative;
+  width: var(--downloads-item-width);
+}
+
+#content.is-active {
+  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .05), 0 1px 4px 0 rgba(0, 0, 0, .08),
+              0 3px 1px -2px rgba(0, 0, 0, .2);
+}
+
+#content:not(.is-active) {
+  opacity: .6;
+}
+
+#details {
+  -webkit-border-start: 1px #d8d8d8 solid;
+  -webkit-padding-end: 16px;
+  -webkit-padding-start: 24px;
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  padding-bottom: 12px;
+  padding-top: 16px;
+}
+
+#content:not(.is-active) #details {
+  color: #bababa;
+}
+
+#content:not(.is-active) #name {
+  text-decoration: line-through;
+}
+
+.icon-wrapper {
+  align-self: center;
+  flex: none;
+  justify-content: center;
+  margin: 0 24px;
+}
+
+.icon {
+  height: 32px;
+  width: 32px;
+}
+
+#content:-webkit-any(.show-progress, .dangerous) #file-icon-wrapper {
+  /* TODO(dbeam): animate from top-aligned to centered when items finish? */
+  align-self: flex-start;
+  padding-top: 16px;
+}
+
+#content:not(.is-active) .icon {
+  -webkit-filter: grayscale(100%);
+  opacity: .5;
+}
+
+#danger-icon {
+  height: 32px;
+  width: 32px;
+}
+
+#danger-icon[icon='warning'] {
+  color: rgb(255, 193, 7);
+}
+
+#danger-icon[icon='remove-circle'] {
+  color: rgb(244, 67, 54);
+}
+
+#name,
+#file-link,
+#url {
+  max-width: 100%;
+}
+
+#name,
+#file-link {
+  font-weight: 500;
+  word-break: break-all;
+}
+
+#name {
+  -webkit-margin-end: 12px;  /* Only really affects #tag. */
+}
+
+.is-active :-webkit-any(#name, #file-link, #pause, #resume, #show) {
+  color: rgb(51, 103, 214);
+}
+
+#tag {
+  color: #5a5a5a;
+  font-weight: 500;
+}
+
+#url {
+  color: inherit;
+  margin-top: 6px;
+  overflow: hidden;
+  text-decoration: none;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.is-active #url {
+  color: #969696;
+}
+
+#progress,
+#description:not(:empty),
+.controls {
+  margin-top: 16px;
+}
+
+.is-active #description {
+  color: #616161;
+}
+
+.dangerous #description {
+  color: rgb(239, 108, 0);
+}
+
+#progress {
+  --paper-progress-active-color: rgb(54, 126, 237);
+  --paper-progress-container-color: rgb(223, 222, 223);
+  width: auto;
+}
+
+.controls {
+  -webkit-margin-start: -.57em;
+}
+
+#cancel,
+#retry,
+.keep,
+.discard {
+  color: #5a5a5a;
+}
+
+#show {
+  margin: .7em .57em;
+}
+
+#controlled-by {
+  -webkit-margin-start: 8px;
+}
+
+#controlled-by,
+#controlled-by a {
+  color: #5a5a5a;
+}
+
+.is-active #controlled-by {
+  color: #333;
+}
+
+.is-active #controlled-by a {
+  color: rgb(51, 103, 214);
+}
+
+#remove-wrapper {
+  align-self: flex-start;
+  margin: 0;
+}
+
+#remove {
+  --iron-icon-height: 16px;
+  --iron-icon-width: 16px;
+  --layout-inline: {
+    /* HACK(dbeam): we probably shouldn't be overriding Polymer like this. */
+  };
+  color: #969696;
+  height: 16px;
+  padding: 8px;
+  width: 16px;
+}
+
+#incognito {
+  bottom: 20px;
+  content: -webkit-image-set(
+      url("chrome://downloads/1x/incognito_marker.png") 1x,
+      url("chrome://downloads/2x/incognito_marker.png") 2x);
+  position: absolute;
+  right: 10px;
+}
+
+</style>
+  </dom-module>
 <dom-module id="paper-item-shared-styles" assetpath="chrome://resources/polymer/v1_0/paper-item/">
   <template>
     <style>
@@ -2927,41 +2954,46 @@
   </template>
 </dom-module>
 <dom-module id="paper-icon-button" assetpath="chrome://resources/polymer/v1_0/paper-icon-button/">
-  <style>
+  <template strip-whitespace="">
+    <style>
+      :host {
+        display: inline-block;
+        position: relative;
+        padding: 8px;
+        outline: none;
+        -webkit-tap-highlight-color: rgba(0,0,0,0);
+        -webkit-user-select: none;
+        -moz-user-select: none;
+        -ms-user-select: none;
+        user-select: none;
+        cursor: pointer;
+        z-index: 0;
 
-    :host {
-      display: inline-block;
-      position: relative;
-      padding: 8px;
-      outline: none;
-      -webkit-tap-highlight-color: rgba(0,0,0,0);
-      -webkit-user-select: none;
-      -moz-user-select: none;
-      -ms-user-select: none;
-      user-select: none;
-      cursor: pointer;
-      z-index: 0;
+        width: 24px;
+        @apply(--paper-icon-button);
+      }
 
-      @apply(--paper-icon-button);
-    }
+      :host #ink {
+        color: var(--paper-icon-button-ink-color, --primary-text-color);
+        opacity: 0.6;
+      }
 
-    :host #ink {
-      color: var(--paper-icon-button-ink-color, --primary-text-color);
-      opacity: 0.6;
-    }
+      :host([disabled]) {
+        color: var(--paper-icon-button-disabled-text, --disabled-text-color);
+        pointer-events: none;
+        cursor: auto;
+        @apply(--paper-icon-button-disabled);
+      }
 
-    :host([disabled]) {
-      color: var(--paper-icon-button-disabled-text, --disabled-text-color);
-      pointer-events: none;
-      cursor: auto;
-      @apply(--paper-icon-button-disabled);
-    }
-  </style>
-  <template>
-    <paper-ripple id="ink" class="circle" center=""></paper-ripple>
+      iron-icon {
+        --iron-icon-width: 100%;
+        --iron-icon-height: 100%;
+      }
+    </style>
     <iron-icon id="icon" src="[[src]]" icon="[[icon]]" alt$="[[alt]]"></iron-icon>
   </template>
-</dom-module>
+
+  </dom-module>
 <dom-module id="paper-input-container" assetpath="chrome://resources/polymer/v1_0/paper-input/">
   <template>
 
@@ -3313,6 +3345,10 @@
   width: var(--downloads-item-width);
 }
 
+:host-context(.loading) #actions {
+  visibility: hidden;
+}
+
 :host(:not([downloads-showing])) #actions {
   justify-content: center;
 }
@@ -3400,8 +3436,13 @@
   <template>
     <paper-header-panel id="panel" class="loading">
       <downloads-toolbar class="paper-header" id="toolbar"></downloads-toolbar>
-      <div id="downloads-list" hidden$="[[!hasDownloads_]]"></div>
-      <div id="no-downloads" hidden$="[[hasDownloads_]]">
+      <iron-list id="downloads-list" items="{{items_}}" hidden="[[!hasDownloads_]]">
+        <template>
+          <downloads-item data="[[item]]" hide-date="[[item.hideDate]]">
+          </downloads-item>
+        </template>
+      </iron-list>
+      <div id="no-downloads" hidden="[[hasDownloads_]]">
         <div>
           <div class="illustration"></div>
           <span></span>
diff --git a/chrome/browser/ui/browser_command_controller_unittest.cc b/chrome/browser/ui/browser_command_controller_unittest.cc
index 69533b358..13ad9d4 100644
--- a/chrome/browser/ui/browser_command_controller_unittest.cc
+++ b/chrome/browser/ui/browser_command_controller_unittest.cc
@@ -26,6 +26,7 @@
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/signin/core/common/signin_pref_names.h"
 #include "content/public/browser/native_web_keyboard_event.h"
+#include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
 typedef BrowserWithTestWindowTest BrowserCommandControllerTest;
@@ -35,54 +36,59 @@
   // F1-3 keys are reserved Chrome accelerators on Chrome OS.
   EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
       IDC_BACK, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_BROWSER_BACK, 0, 0)));
+                    ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK,
+                                 ui::DomCode::BROWSER_BACK, 0))));
   EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_FORWARD, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_BROWSER_FORWARD, 0, 0)));
+      IDC_FORWARD, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                       ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_FORWARD,
+                       ui::DomCode::BROWSER_FORWARD, 0))));
   EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
       IDC_RELOAD, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_BROWSER_REFRESH, 0, 0)));
+                      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_REFRESH,
+                                   ui::DomCode::BROWSER_REFRESH, 0))));
 
   // When there are modifier keys pressed, don't reserve.
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_RELOAD_IGNORING_CACHE, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_F3, ui::EF_SHIFT_DOWN, 0)));
+      IDC_RELOAD_IGNORING_CACHE, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                                     ui::ET_KEY_PRESSED, ui::VKEY_F3,
+                                     ui::DomCode::F3, ui::EF_SHIFT_DOWN))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_RELOAD_IGNORING_CACHE, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_F3, ui::EF_CONTROL_DOWN, 0)));
+      IDC_RELOAD_IGNORING_CACHE, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                                     ui::ET_KEY_PRESSED, ui::VKEY_F3,
+                                     ui::DomCode::F3, ui::EF_CONTROL_DOWN))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
       IDC_FULLSCREEN, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_F4, ui::EF_SHIFT_DOWN, 0)));
+                          ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_F4,
+                                       ui::DomCode::F4, ui::EF_SHIFT_DOWN))));
 
   // F4-10 keys are not reserved since they are Ash accelerators.
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                          ui::VKEY_F4, 0, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F4, ui::DomCode::F4, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                          ui::VKEY_F5, 0, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F5, ui::DomCode::F5, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                          ui::VKEY_F6, 0, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F6, ui::DomCode::F6, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                          ui::VKEY_F7, 0, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F7, ui::DomCode::F7, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                          ui::VKEY_F8, 0, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F8, ui::DomCode::F8, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                          ui::VKEY_F9, 0, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F9, ui::DomCode::F9, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                          ui::VKEY_F10, 0, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F10, ui::DomCode::F10, 0))));
 
   // Shift+Control+Alt+F3 is also an Ash accelerator. Don't reserve it.
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false,
-          ui::VKEY_F3,
-          ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F3, ui::DomCode::F3,
+              ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN))));
 #endif  // OS_CHROMEOS
 
 #if defined(USE_AURA)
@@ -91,14 +97,17 @@
   // The content::NativeWebKeyboardEvent constructor is available only when
   // USE_AURA is #defined.
   EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_NEW_WINDOW, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_N, ui::EF_CONTROL_DOWN, 0)));
+      IDC_NEW_WINDOW, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                          ui::ET_KEY_PRESSED, ui::VKEY_N, ui::DomCode::KEY_N,
+                          ui::EF_CONTROL_DOWN))));
   EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_CLOSE_TAB, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_W, ui::EF_CONTROL_DOWN, 0)));
+      IDC_CLOSE_TAB, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                         ui::ET_KEY_PRESSED, ui::VKEY_W, ui::DomCode::KEY_W,
+                         ui::EF_CONTROL_DOWN))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
       IDC_FIND, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_F, ui::EF_CONTROL_DOWN, 0)));
+                    ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_F,
+                                 ui::DomCode::KEY_F, ui::EF_CONTROL_DOWN))));
 #endif  // USE_AURA
 }
 
@@ -109,31 +118,34 @@
   // When is_app(), no keys are reserved.
 #if defined(OS_CHROMEOS)
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_BACK, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                                ui::VKEY_F1, 0, 0)));
+      IDC_BACK, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                    ui::ET_KEY_PRESSED, ui::VKEY_F1, ui::DomCode::F1, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_FORWARD, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                                   ui::VKEY_F2, 0, 0)));
+      IDC_FORWARD, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                       ui::ET_KEY_PRESSED, ui::VKEY_F2, ui::DomCode::F2, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_RELOAD, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                                  ui::VKEY_F3, 0, 0)));
+      IDC_RELOAD, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                      ui::ET_KEY_PRESSED, ui::VKEY_F3, ui::DomCode::F3, 0))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      -1, content::NativeWebKeyboardEvent(ui::ET_KEY_PRESSED, false,
-                                          ui::VKEY_F4, 0, 0)));
+      -1, content::NativeWebKeyboardEvent(ui::KeyEvent(
+              ui::ET_KEY_PRESSED, ui::VKEY_F4, ui::DomCode::F4, 0))));
 #endif  // OS_CHROMEOS
 
 #if defined(USE_AURA)
   // The content::NativeWebKeyboardEvent constructor is available only when
   // USE_AURA is #defined.
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_NEW_WINDOW, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_N, ui::EF_CONTROL_DOWN, 0)));
+      IDC_NEW_WINDOW, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                          ui::ET_KEY_PRESSED, ui::VKEY_N, ui::DomCode::KEY_N,
+                          ui::EF_CONTROL_DOWN))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_CLOSE_TAB, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_W, ui::EF_CONTROL_DOWN, 0)));
+      IDC_CLOSE_TAB, content::NativeWebKeyboardEvent(ui::KeyEvent(
+                         ui::ET_KEY_PRESSED, ui::VKEY_W, ui::DomCode::KEY_W,
+                         ui::EF_CONTROL_DOWN))));
   EXPECT_FALSE(browser()->command_controller()->IsReservedCommandOrKey(
       IDC_FIND, content::NativeWebKeyboardEvent(
-          ui::ET_KEY_PRESSED, false, ui::VKEY_F, ui::EF_CONTROL_DOWN, 0)));
+                    ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_F,
+                                 ui::DomCode::KEY_F, ui::EF_CONTROL_DOWN))));
 #endif  // USE_AURA
 }
 
diff --git a/chrome/browser/ui/panels/panel_browsertest.cc b/chrome/browser/ui/panels/panel_browsertest.cc
index e7e58af..639e101 100644
--- a/chrome/browser/ui/panels/panel_browsertest.cc
+++ b/chrome/browser/ui/panels/panel_browsertest.cc
@@ -44,7 +44,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/events/event.h"
 #include "ui/events/event_utils.h"
+#include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/gfx/screen.h"
 
 using content::WebContents;
@@ -1696,21 +1698,10 @@
   content::WindowedNotificationObserver signal(
       chrome::NOTIFICATION_PANEL_CLOSED,
       content::Source<Panel>(panel));
-#if defined(USE_AURA)
-  double now = ui::EventTimeForNow().InSecondsF();
-  content::NativeWebKeyboardEvent key_event(
-      ui::ET_KEY_PRESSED,
-      false,
-      ui::VKEY_W,
-      ui::EF_CONTROL_DOWN,
-      now);
-#elif defined(OS_WIN)
-  ::MSG key_msg = { NULL, WM_KEYDOWN, ui::VKEY_W, 0 };
-  content::NativeWebKeyboardEvent key_event(key_msg);
-  key_event.modifiers = content::NativeWebKeyboardEvent::ControlKey;
-#else
-  content::NativeWebKeyboardEvent key_event;
-#endif
+
+  ui::KeyEvent ui_event(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::DomCode::KEY_W,
+                        ui::EF_CONTROL_DOWN);
+  content::NativeWebKeyboardEvent key_event(ui_event);
   panel->HandleKeyboardEvent(key_event);
   signal.Wait();
   EXPECT_EQ(0, panel_manager->num_panels());
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
index ee5e257..dba5638 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -10,6 +10,8 @@
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/keycodes/dom/dom_code.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/input_method/input_method_configuration.h"
@@ -115,11 +117,17 @@
 // Checks that a single change of the text in the omnibox invokes
 // only one call to OmniboxViewViews::UpdatePopup().
 TEST_F(OmniboxViewViewsTest, UpdatePopupCall) {
-  omnibox_textfield()->InsertChar('a', 0);
+  ui::KeyEvent char_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::KEY_A, 0,
+                          ui::DomKey::FromCharacter('a'),
+                          ui::EventTimeForNow());
+  omnibox_textfield()->InsertChar(char_event);
   omnibox_view()->CheckUpdatePopupCallInfo(
       1, base::ASCIIToUTF16("a"), gfx::Range(1));
 
-  omnibox_textfield()->InsertChar('b', 0);
+  char_event =
+      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, ui::DomCode::KEY_B, 0,
+                   ui::DomKey::FromCharacter('b'), ui::EventTimeForNow());
+  omnibox_textfield()->InsertChar(char_event);
   omnibox_view()->CheckUpdatePopupCallInfo(
       2, base::ASCIIToUTF16("ab"), gfx::Range(2));
 
diff --git a/chrome/browser/ui/webui/options/font_settings_handler.cc b/chrome/browser/ui/webui/options/font_settings_handler.cc
index 865e6715..690e053 100644
--- a/chrome/browser/ui/webui/options/font_settings_handler.cc
+++ b/chrome/browser/ui/webui/options/font_settings_handler.cc
@@ -64,7 +64,8 @@
 namespace options {
 
 FontSettingsHandler::FontSettingsHandler()
-    : extension_registry_observer_(this) {
+    : extension_registry_observer_(this),
+      weak_ptr_factory_(this) {
 }
 
 FontSettingsHandler::~FontSettingsHandler() {
@@ -190,7 +191,7 @@
 void FontSettingsHandler::HandleFetchFontsData(const base::ListValue* args) {
   content::GetFontListAsync(
       base::Bind(&FontSettingsHandler::FontsListHasLoaded,
-                 base::Unretained(this)));
+                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void FontSettingsHandler::FontsListHasLoaded(
diff --git a/chrome/browser/ui/webui/options/font_settings_handler.h b/chrome/browser/ui/webui/options/font_settings_handler.h
index 98da3f3..171d731 100644
--- a/chrome/browser/ui/webui/options/font_settings_handler.h
+++ b/chrome/browser/ui/webui/options/font_settings_handler.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_HANDLER_H_
 
+#include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/prefs/pref_member.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/ui/webui/options/options_ui.h"
@@ -80,6 +82,8 @@
                  extensions::ExtensionRegistryObserver>
       extension_registry_observer_;
 
+  base::WeakPtrFactory<FontSettingsHandler> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(FontSettingsHandler);
 };
 
diff --git a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
index 8befb1a..46f3d744 100644
--- a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
@@ -8,8 +8,6 @@
 var fileSystemProviderInternal =
     require('binding').Binding.create('fileSystemProviderInternal').generate();
 var eventBindings = require('event_bindings');
-var fileSystemNatives = requireNative('file_system_natives');
-var GetDOMError = fileSystemNatives.GetDOMError;
 
 /**
  * Maximum size of the thumbnail in bytes.
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 1334527b..d8af78d 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-7544.0.0
\ No newline at end of file
+7546.0.0
\ No newline at end of file
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 699a72ad..3b326924 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1355,8 +1355,7 @@
                                    extract_mask, form, field);
 }
 
-std::vector<WebFormControlElement>
-GetUnownedAutofillableFormFieldElements(
+std::vector<WebFormControlElement> GetUnownedFormFieldElements(
     const WebElementCollection& elements,
     std::vector<WebElement>* fieldsets) {
   std::vector<WebFormControlElement> unowned_fieldset_children;
@@ -1374,7 +1373,14 @@
       fieldsets->push_back(element);
     }
   }
-  return ExtractAutofillableElementsFromSet(unowned_fieldset_children);
+  return unowned_fieldset_children;
+}
+
+std::vector<WebFormControlElement> GetUnownedAutofillableFormFieldElements(
+    const WebElementCollection& elements,
+    std::vector<WebElement>* fieldsets) {
+  return ExtractAutofillableElementsFromSet(
+      GetUnownedFormFieldElements(elements, fieldsets));
 }
 
 bool UnownedCheckoutFormElementsAndFieldSetsToFormData(
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h
index 04b1b7c..97872d1c 100644
--- a/components/autofill/content/renderer/form_autofill_util.h
+++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -142,6 +142,12 @@
 // Get all form control elements from |elements| that are not part of a form.
 // If |fieldsets| is not NULL, also append the fieldsets encountered that are
 // not part of a form.
+std::vector<blink::WebFormControlElement> GetUnownedFormFieldElements(
+    const blink::WebElementCollection& elements,
+    std::vector<blink::WebElement>* fieldsets);
+
+// A shorthand for filtering the results of GetUnownedFormFieldElements with
+// ExtractAutofillableElementsFromSet.
 std::vector<blink::WebFormControlElement>
 GetUnownedAutofillableFormFieldElements(
     const blink::WebElementCollection& elements,
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index c7a3e05..dc87799d 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -43,6 +43,8 @@
   ~SyntheticForm();
 
   std::vector<blink::WebElement> fieldsets;
+  // Contains control elements of the represented form, including not fillable
+  // ones.
   std::vector<blink::WebFormControlElement> control_elements;
   blink::WebDocument document;
   blink::WebString action;
@@ -606,9 +608,8 @@
     const ModifiedValues* nonscript_modified_values,
     const FormsPredictionsMap* form_predictions) {
   SyntheticForm synthetic_form;
-  synthetic_form.control_elements =
-      form_util::GetUnownedAutofillableFormFieldElements(
-          frame.document().all(), &synthetic_form.fieldsets);
+  synthetic_form.control_elements = form_util::GetUnownedFormFieldElements(
+      frame.document().all(), &synthetic_form.fieldsets);
   synthetic_form.document = frame.document();
 
   if (synthetic_form.control_elements.empty())
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
index 19f3e25b4..6d62e64a 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -24,6 +24,13 @@
 const char kUMAProxyStartupStateHistogram[] =
     "DataReductionProxy.StartupState";
 
+void RecordSettingsEnabledState(
+    data_reduction_proxy::DataReductionSettingsEnabledAction action) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "DataReductionProxy.EnabledState", action,
+      data_reduction_proxy::DATA_REDUCTION_SETTINGS_ACTION_BOUNDARY);
+}
+
 }  // namespace
 
 namespace data_reduction_proxy {
@@ -254,6 +261,12 @@
     prefs->SetBoolean(prefs::kDataReductionProxyWasEnabledBefore, true);
     ResetDataReductionStatistics();
   }
+  if (!at_startup) {
+    if (IsDataReductionProxyEnabled())
+      RecordSettingsEnabledState(DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON);
+    else
+      RecordSettingsEnabledState(DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF);
+  }
   // Configure use of the data reduction proxy if it is enabled.
   if (at_startup && !data_reduction_proxy_service_->Initialized())
     deferred_initialization_ = true;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
index c6d6974..45d4f1c 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -55,6 +55,15 @@
   LO_FI_OPT_OUT_ACTION_INDEX_BOUNDARY,
 };
 
+// Values of the UMA DataReductionProxy.EnabledState histogram.
+// This enum must remain synchronized with DataReductionProxyEnabledState
+// in metrics/histograms/histograms.xml.
+enum DataReductionSettingsEnabledAction {
+  DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON = 0,
+  DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF,
+  DATA_REDUCTION_SETTINGS_ACTION_BOUNDARY,
+};
+
 // Central point for configuring the data reduction proxy.
 // This object lives on the UI thread and all of its methods are expected to
 // be called from there.
@@ -236,6 +245,8 @@
                            TestLoFiImplicitOptOutHistograms);
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
                            TestLoFiSessionStateHistograms);
+  FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
+                           TestSettingsEnabledStateHistograms);
 
   // Override of DataReductionProxyService::Observer.
   void OnServiceInitialized() override;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
index 2c1b10d..28e63104 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
@@ -628,6 +628,33 @@
       kUMALoFiSessionState, settings_->lo_fi_consecutive_session_disables_ + 3);
 }
 
+TEST_F(DataReductionProxySettingsTest, TestSettingsEnabledStateHistograms) {
+  const char kUMAEnabledState[] = "DataReductionProxy.EnabledState";
+  base::HistogramTester histogram_tester;
+
+  settings_->InitPrefMembers();
+  settings_->data_reduction_proxy_service_->SetIOData(
+      test_context_->io_data()->GetWeakPtr());
+
+  // No settings state histograms should be recorded during startup.
+  test_context_->RunUntilIdle();
+  histogram_tester.ExpectTotalCount(kUMAEnabledState, 0);
+
+  settings_->SetDataReductionProxyEnabled(true);
+  test_context_->RunUntilIdle();
+  histogram_tester.ExpectBucketCount(
+      kUMAEnabledState, DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON, 1);
+  histogram_tester.ExpectBucketCount(
+      kUMAEnabledState, DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF, 0);
+
+  settings_->SetDataReductionProxyEnabled(false);
+  test_context_->RunUntilIdle();
+  histogram_tester.ExpectBucketCount(
+      kUMAEnabledState, DATA_REDUCTION_SETTINGS_ACTION_OFF_TO_ON, 1);
+  histogram_tester.ExpectBucketCount(
+      kUMAEnabledState, DATA_REDUCTION_SETTINGS_ACTION_ON_TO_OFF, 1);
+}
+
 TEST_F(DataReductionProxySettingsTest, TestGetDailyContentLengths) {
   ContentLengthList result =
       settings_->GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength);
diff --git a/components/history/core/browser/visit_database_unittest.cc b/components/history/core/browser/visit_database_unittest.cc
index 8074dd9..936bb24c 100644
--- a/components/history/core/browser/visit_database_unittest.cc
+++ b/components/history/core/browser/visit_database_unittest.cc
@@ -417,16 +417,12 @@
 }
 
 TEST_F(VisitDatabaseTest, GetHistoryCount) {
-  Time today = Time::Now().LocalMidnight();
-  // Find the beginning of yesterday and the day before yesterday. We cannot use
-  // TimeDelta::FromDays(1), as this simply removes 24 hours and thus does not
-  // work correctly with DST shifts. Instead, we'll jump 36 hours (i.e.
-  // somewhere in the middle of the previous day), and use |LocalMidnight()| to
-  // round down to the beginning of the day in the local time, taking timezones
-  // and DST into account. This is necessary to achieve the same equivalence
-  // class on days as the DATE(..., 'localtime') function in SQL.
-  Time yesterday = (today - TimeDelta::FromHours(36)).LocalMidnight();
-  Time two_days_ago = (yesterday - TimeDelta::FromHours(36)).LocalMidnight();
+  // Start with a day in the middle of summer, so that we are nowhere near
+  // DST shifts.
+  Time today;
+  ASSERT_TRUE(Time::FromString("2015-07-07", &today));
+  Time yesterday = today - TimeDelta::FromDays(1);
+  Time two_days_ago = yesterday - TimeDelta::FromDays(1);
   Time now = two_days_ago;
 
   ui::PageTransition standard_transition = ui::PageTransitionFromInt(
@@ -526,7 +522,14 @@
   EXPECT_EQ(0, result);
 
   // If this timezone uses DST, test the behavior on days when the time
-  // is shifted forward and backward.
+  // is shifted forward and backward. Note that in this case we cannot use
+  // TimeDelta::FromDays(1) to move one day, as this simply removes 24 hours and
+  // thus does not work correctly with DST shifts. Instead, we'll go back
+  // 1 second (i.e. somewhere in the middle of the previous day), and use
+  // |LocalMidnight()| to round down to the beginning of the day in the local
+  // time, taking timezones and DST into account. This is necessary to achieve
+  // the same equivalence class on days as the DATE(..., 'localtime') function
+  // in SQL.
   Time shift_forward;
   Time shift_backward;
   Time current_day = (two_days_ago - TimeDelta::FromSeconds(1)).LocalMidnight();
diff --git a/components/html_viewer/web_layer_tree_view_impl.cc b/components/html_viewer/web_layer_tree_view_impl.cc
index 0870aa8..9b1f468 100644
--- a/components/html_viewer/web_layer_tree_view_impl.cc
+++ b/components/html_viewer/web_layer_tree_view_impl.cc
@@ -91,11 +91,7 @@
 void WebLayerTreeViewImpl::BeginMainFrame(const cc::BeginFrameArgs& args) {
   VLOG(2) << "WebLayerTreeViewImpl::BeginMainFrame";
   double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF();
-  double deadline_sec = (args.deadline - base::TimeTicks()).InSecondsF();
-  double interval_sec = args.interval.InSecondsF();
-  blink::WebBeginFrameArgs web_begin_frame_args(
-      frame_time_sec, deadline_sec, interval_sec);
-  widget_->beginFrame(web_begin_frame_args);
+  widget_->beginFrame(frame_time_sec);
   layer_tree_host_->SetNeedsAnimate();
 }
 
diff --git a/components/scheduler/base/task_queue_manager_perftest.cc b/components/scheduler/base/task_queue_manager_perftest.cc
index 66e2e32..473ed0df 100644
--- a/components/scheduler/base/task_queue_manager_perftest.cc
+++ b/components/scheduler/base/task_queue_manager_perftest.cc
@@ -24,6 +24,11 @@
         num_tasks_to_post_(0),
         num_tasks_to_run_(0) {}
 
+  void SetUp() override {
+    if (base::ThreadTicks::IsSupported())
+      base::ThreadTicks::WaitUntilInitialized();
+  }
+
   void Initialize(size_t num_queues) {
     num_queues_ = num_queues;
     message_loop_.reset(new base::MessageLoop());
diff --git a/components/test_runner/web_test_proxy.cc b/components/test_runner/web_test_proxy.cc
index bc55b0c..3601ff505 100644
--- a/components/test_runner/web_test_proxy.cc
+++ b/components/test_runner/web_test_proxy.cc
@@ -761,16 +761,11 @@
 void WebTestProxyBase::AnimateNow() {
   if (animate_scheduled_) {
     base::TimeDelta animate_time = base::TimeTicks::Now() - base::TimeTicks();
-    base::TimeDelta interval = base::TimeDelta::FromMicroseconds(16666);
-    blink::WebBeginFrameArgs args(animate_time.InSecondsF(),
-                                  (animate_time + interval).InSecondsF(),
-                                  interval.InSecondsF());
-
     animate_scheduled_ = false;
-    web_widget_->beginFrame(args);
+    web_widget_->beginFrame(animate_time.InSecondsF());
     web_widget_->layout();
     if (blink::WebPagePopup* popup = web_widget_->pagePopup()) {
-      popup->beginFrame(args);
+      popup->beginFrame(animate_time.InSecondsF());
       popup->layout();
     }
   }
diff --git a/content/browser/renderer_host/input/web_input_event_builders_win.cc b/content/browser/renderer_host/input/web_input_event_builders_win.cc
index 9d41701..88aa3e8 100644
--- a/content/browser/renderer_host/input/web_input_event_builders_win.cc
+++ b/content/browser/renderer_host/input/web_input_event_builders_win.cc
@@ -69,8 +69,7 @@
     result.text[0] = result.windowsKeyCode;
     result.unmodifiedText[0] = result.windowsKeyCode;
   }
-  if (result.type != WebInputEvent::Char)
-    result.setKeyIdentifierFromWindowsKeyCode();
+  result.setKeyIdentifierFromWindowsKeyCode();
 
   if (::GetKeyState(VK_SHIFT) & 0x8000)
     result.modifiers |= WebInputEvent::ShiftKey;
diff --git a/content/browser/renderer_host/native_web_keyboard_event_aura.cc b/content/browser/renderer_host/native_web_keyboard_event_aura.cc
index 5f3d8bd9..b84c879b 100644
--- a/content/browser/renderer_host/native_web_keyboard_event_aura.cc
+++ b/content/browser/renderer_host/native_web_keyboard_event_aura.cc
@@ -19,14 +19,6 @@
   return event ? ui::Event::Clone(*event).release() : nullptr;
 }
 
-int EventFlagsToWebInputEventModifiers(int flags) {
-  return
-      (flags & ui::EF_SHIFT_DOWN ? blink::WebInputEvent::ShiftKey : 0) |
-      (flags & ui::EF_CONTROL_DOWN ? blink::WebInputEvent::ControlKey : 0) |
-      (flags & ui::EF_CAPS_LOCK_DOWN ? blink::WebInputEvent::CapsLockOn : 0) |
-      (flags & ui::EF_ALT_DOWN ? blink::WebInputEvent::AltKey : 0);
-}
-
 }  // namespace
 
 using blink::WebKeyboardEvent;
@@ -58,34 +50,16 @@
       match_edit_command(false) {
 }
 
-NativeWebKeyboardEvent::NativeWebKeyboardEvent(
-    ui::EventType key_event_type,
-    bool is_char,
-    wchar_t character,
-    int state,
-    double time_stamp_seconds)
-    : os_event(NULL),
+NativeWebKeyboardEvent::NativeWebKeyboardEvent(const ui::KeyEvent& key_event,
+                                               base::char16 character)
+    : WebKeyboardEvent(MakeWebKeyboardEvent(key_event)),
+      os_event(NULL),
       skip_in_browser(false),
       match_edit_command(false) {
-  switch (key_event_type) {
-    case ui::ET_KEY_PRESSED:
-      type = is_char ? blink::WebInputEvent::Char :
-          blink::WebInputEvent::RawKeyDown;
-      break;
-    case ui::ET_KEY_RELEASED:
-      type = blink::WebInputEvent::KeyUp;
-      break;
-    default:
-      NOTREACHED();
-  }
-
-  modifiers = EventFlagsToWebInputEventModifiers(state);
-  timeStampSeconds = time_stamp_seconds;
+  type = blink::WebInputEvent::Char;
   windowsKeyCode = character;
-  nativeKeyCode = character;
   text[0] = character;
   unmodifiedText[0] = character;
-  isSystemKey = ui::IsSystemKeyModifier(state);
   setKeyIdentifierFromWindowsKeyCode();
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index c7aac43..96cfc510 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1607,22 +1607,17 @@
   has_composition_text_ = false;
 }
 
-void RenderWidgetHostViewAura::InsertChar(base::char16 ch, int flags) {
+void RenderWidgetHostViewAura::InsertChar(const ui::KeyEvent& event) {
   if (popup_child_host_view_ && popup_child_host_view_->NeedsInputGrab()) {
-    popup_child_host_view_->InsertChar(ch, flags);
+    popup_child_host_view_->InsertChar(event);
     return;
   }
 
   // Ignore character messages for VKEY_RETURN sent on CTRL+M. crbug.com/315547
-  if (host_ && (accept_return_character_ || ch != ui::VKEY_RETURN)) {
-    double now = ui::EventTimeForNow().InSecondsF();
+  if (host_ &&
+      (accept_return_character_ || event.GetCharacter() != ui::VKEY_RETURN)) {
     // Send a blink::WebInputEvent::Char event to |host_|.
-    NativeWebKeyboardEvent webkit_event(ui::ET_KEY_PRESSED,
-                                        true /* is_char */,
-                                        ch,
-                                        flags,
-                                        now);
-    ForwardKeyboardEvent(webkit_event);
+    ForwardKeyboardEvent(NativeWebKeyboardEvent(event, event.GetCharacter()));
   }
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index 99bb3ce..8cc79db 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -201,7 +201,7 @@
   void ConfirmCompositionText() override;
   void ClearCompositionText() override;
   void InsertText(const base::string16& text) override;
-  void InsertChar(base::char16 ch, int flags) override;
+  void InsertChar(const ui::KeyEvent& event) override;
   ui::TextInputType GetTextInputType() const override;
   ui::TextInputMode GetTextInputMode() const override;
   int GetTextInputFlags() const override;
diff --git a/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
index 3e16918..df02f17 100644
--- a/content/browser/renderer_host/render_widget_host_view_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
@@ -329,8 +329,14 @@
 
 // Test basic frame subscription functionality.  We subscribe, and then run
 // until at least one DeliverFrameCallback has been invoked.
+// https://crbug.com/542896
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_FrameSubscriberTest DISABLED_FrameSubscriberTest
+#else
+#define MAYBE_FrameSubscriberTest FrameSubscriberTest
+#endif
 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest,
-                       FrameSubscriberTest) {
+                       MAYBE_FrameSubscriberTest) {
   SET_UP_SURFACE_OR_PASS_TEST(NULL);
   RenderWidgetHostViewBase* const view = GetRenderWidgetHostView();
 
@@ -698,8 +704,16 @@
   std::string test_url_;
 };
 
+// https://crbug.com/542896
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_CopyFromCompositingSurface_Origin_Unscaled \
+  DISABLED_CopyFromCompositingSurface_Origin_Unscaled
+#else
+#define MAYBE_CopyFromCompositingSurface_Origin_Unscaled \
+  CopyFromCompositingSurface_Origin_Unscaled
+#endif
 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
-                       CopyFromCompositingSurface_Origin_Unscaled) {
+                       MAYBE_CopyFromCompositingSurface_Origin_Unscaled) {
   gfx::Rect copy_rect(400, 300);
   gfx::Size output_size = copy_rect.size();
   gfx::Size html_rect_size(400, 300);
@@ -710,8 +724,16 @@
                                 video_frame);
 }
 
+// https://crbug.com/542896
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_CopyFromCompositingSurface_Origin_Scaled \
+  DISABLED_FrameSubscriberTestCopyFromCompositingSurface_Origin_Scaled
+#else
+#define MAYBE_CopyFromCompositingSurface_Origin_Scaled \
+  CopyFromCompositingSurface_Origin_Scaled
+#endif
 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
-                       CopyFromCompositingSurface_Origin_Scaled) {
+                       MAYBE_CopyFromCompositingSurface_Origin_Scaled) {
   gfx::Rect copy_rect(400, 300);
   gfx::Size output_size(200, 100);
   gfx::Size html_rect_size(400, 300);
@@ -722,8 +744,16 @@
                                 video_frame);
 }
 
+// https://crbug.com/542896
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_CopyFromCompositingSurface_Cropped_Unscaled \
+  DISABLED_CopyFromCompositingSurface_Cropped_Unscaled
+#else
+#define MAYBE_CopyFromCompositingSurface_Cropped_Unscaled \
+  CopyFromCompositingSurface_Cropped_Unscaled
+#endif
 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
-                       CopyFromCompositingSurface_Cropped_Unscaled) {
+                       MAYBE_CopyFromCompositingSurface_Cropped_Unscaled) {
   // Grab 60x60 pixels from the center of the tab contents.
   gfx::Rect copy_rect(400, 300);
   copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(30, 30),
@@ -737,8 +767,16 @@
                                 video_frame);
 }
 
+// https://crbug.com/542896
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_CopyFromCompositingSurface_Cropped_Scaled \
+  DISABLED_CopyFromCompositingSurface_Cropped_Scaled
+#else
+#define MAYBE_CopyFromCompositingSurface_Cropped_Scaled \
+  CopyFromCompositingSurface_Cropped_Scaled
+#endif
 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
-                       CopyFromCompositingSurface_Cropped_Scaled) {
+                       MAYBE_CopyFromCompositingSurface_Cropped_Scaled) {
   // Grab 60x60 pixels from the center of the tab contents.
   gfx::Rect copy_rect(400, 300);
   copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(30, 30),
@@ -752,8 +790,16 @@
                                 video_frame);
 }
 
+// https://crbug.com/542896
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_CopyFromCompositingSurface_ForVideoFrame \
+  DISABLED_CopyFromCompositingSurface_ForVideoFrame
+#else
+#define MAYBE_CopyFromCompositingSurface_ForVideoFrame \
+  CopyFromCompositingSurface_ForVideoFrame
+#endif
 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
-                       CopyFromCompositingSurface_ForVideoFrame) {
+                       MAYBE_CopyFromCompositingSurface_ForVideoFrame) {
   // Grab 90x60 pixels from the center of the tab contents.
   gfx::Rect copy_rect(400, 300);
   copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(45, 30),
@@ -767,8 +813,16 @@
                                 video_frame);
 }
 
+// https://crbug.com/542896
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_CopyFromCompositingSurface_ForVideoFrame_Scaled \
+  DISABLED_CopyFromCompositingSurface_ForVideoFrame_Scaled
+#else
+#define MAYBE_CopyFromCompositingSurface_ForVideoFrame_Scaled \
+  CopyFromCompositingSurface_ForVideoFrame_Scaled
+#endif
 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture,
-                       CopyFromCompositingSurface_ForVideoFrame_Scaled) {
+                       MAYBE_CopyFromCompositingSurface_ForVideoFrame_Scaled) {
   // Grab 90x60 pixels from the center of the tab contents.
   gfx::Rect copy_rect(400, 300);
   copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(45, 30),
@@ -819,7 +873,8 @@
 
 // NineImagePainter implementation crashes the process on Windows when this
 // content_browsertest forces a device scale factor.  http://crbug.com/399349
-#if defined(OS_WIN)
+// Disabled under MSAN.  http://crbug.com/542896
+#if defined(OS_WIN) || defined(MEMORY_SANITIZER)
 #define MAYBE_CopyToBitmap_EntireRegion DISABLED_CopyToBitmap_EntireRegion
 #define MAYBE_CopyToBitmap_CenterRegion DISABLED_CopyToBitmap_CenterRegion
 #define MAYBE_CopyToBitmap_ScaledResult DISABLED_CopyToBitmap_ScaledResult
diff --git a/content/browser/service_worker/service_worker_cache_writer.cc b/content/browser/service_worker/service_worker_cache_writer.cc
index e934300..20c6987 100644
--- a/content/browser/service_worker/service_worker_cache_writer.cc
+++ b/content/browser/service_worker/service_worker_cache_writer.cc
@@ -109,7 +109,7 @@
         state_ = STATE_DONE;
         break;
     }
-  } while (status >= net::OK && state_ != STATE_DONE);
+  } while (status != net::ERR_IO_PENDING && state_ != STATE_DONE);
   io_pending_ = (status == net::ERR_IO_PENDING);
   return status;
 }
@@ -209,6 +209,7 @@
 }
 
 int ServiceWorkerCacheWriter::DoReadHeadersForCompare(int result) {
+  DCHECK_GE(result, 0);
   DCHECK(headers_to_write_);
 
   headers_to_read_ = new HttpResponseInfoIOBuffer;
@@ -228,6 +229,7 @@
 }
 
 int ServiceWorkerCacheWriter::DoReadDataForCompare(int result) {
+  DCHECK_GE(result, 0);
   DCHECK(data_to_write_);
 
   data_to_read_ = new net::IOBuffer(len_to_write_);
@@ -244,13 +246,14 @@
   DCHECK(data_to_read_);
   DCHECK(data_to_write_);
   DCHECK_EQ(len_to_read_, len_to_write_);
-  DCHECK_LE(result + compare_offset_, static_cast<size_t>(len_to_write_));
 
   if (result < 0) {
     state_ = STATE_DONE;
     return result;
   }
 
+  DCHECK_LE(result + compare_offset_, static_cast<size_t>(len_to_write_));
+
   // Premature EOF while reading the service worker script cache data to
   // compare. Fail the comparison.
   if (result == 0 && len_to_write_ != 0) {
@@ -302,6 +305,7 @@
 }
 
 int ServiceWorkerCacheWriter::DoReadHeadersForCopy(int result) {
+  DCHECK_GE(result, 0);
   bytes_copied_ = 0;
   copy_reader_ = reader_creator_.Run();
   headers_to_read_ = new HttpResponseInfoIOBuffer;
@@ -325,6 +329,7 @@
 // Also note that this *discards* the read headers and replaces them with the
 // net headers.
 int ServiceWorkerCacheWriter::DoWriteHeadersForCopy(int result) {
+  DCHECK_GE(result, 0);
   DCHECK(!writer_);
   writer_ = writer_creator_.Run();
   state_ = STATE_WRITE_HEADERS_FOR_COPY_DONE;
@@ -341,6 +346,7 @@
 }
 
 int ServiceWorkerCacheWriter::DoReadDataForCopy(int result) {
+  DCHECK_GE(result, 0);
   size_t to_read = std::min(kCopyBufferSize, bytes_compared_ - bytes_copied_);
   // At this point, all compared bytes have been read. Currently
   // |data_to_write_| and |len_to_write_| hold the chunk of network input that
@@ -381,6 +387,7 @@
 }
 
 int ServiceWorkerCacheWriter::DoWriteHeadersForPassthrough(int result) {
+  DCHECK_GE(result, 0);
   writer_ = writer_creator_.Run();
   state_ = STATE_WRITE_HEADERS_FOR_PASSTHROUGH_DONE;
   return WriteInfoHelper(writer_, headers_to_write_.get());
@@ -388,10 +395,11 @@
 
 int ServiceWorkerCacheWriter::DoWriteHeadersForPassthroughDone(int result) {
   state_ = STATE_DONE;
-  return net::OK;
+  return result;
 }
 
 int ServiceWorkerCacheWriter::DoWriteDataForPassthrough(int result) {
+  DCHECK_GE(result, 0);
   state_ = STATE_WRITE_DATA_FOR_PASSTHROUGH_DONE;
   if (len_to_write_ > 0)
     result = WriteDataHelper(writer_, data_to_write_.get(), len_to_write_);
@@ -410,7 +418,7 @@
 
 int ServiceWorkerCacheWriter::DoDone(int result) {
   state_ = STATE_DONE;
-  return net::OK;
+  return result;
 }
 
 // These helpers adapt the AppCache "always use the callback" pattern to the
diff --git a/content/browser/service_worker/service_worker_cache_writer_unittest.cc b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
index c58b57b5..887cedc 100644
--- a/content/browser/service_worker/service_worker_cache_writer_unittest.cc
+++ b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
@@ -205,7 +205,9 @@
 
   // Enqueue expected writes.
   void ExpectWriteInfoOk(size_t len, bool async);
+  void ExpectWriteInfo(size_t len, bool async, int result);
   void ExpectWriteDataOk(size_t len, bool async);
+  void ExpectWriteData(size_t len, bool async, int result);
 
   // Complete a pending asynchronous write. This method DCHECKs unless there is
   // a pending write (a write for which WriteInfo() or WriteData() has been
@@ -239,8 +241,10 @@
   DCHECK(!expected_writes_.empty());
   ExpectedWrite write = expected_writes_.front();
   EXPECT_TRUE(write.is_info);
-  EXPECT_EQ(write.length, static_cast<size_t>(info_buf->response_data_size));
-  info_written_ += info_buf->response_data_size;
+  if (write.result > 0) {
+    EXPECT_EQ(write.length, static_cast<size_t>(info_buf->response_data_size));
+    info_written_ += info_buf->response_data_size;
+  }
   if (!write.async) {
     expected_writes_.pop();
     callback.Run(write.result);
@@ -256,8 +260,10 @@
   DCHECK(!expected_writes_.empty());
   ExpectedWrite write = expected_writes_.front();
   EXPECT_FALSE(write.is_info);
-  EXPECT_EQ(write.length, static_cast<size_t>(buf_len));
-  data_written_ += buf_len;
+  if (write.result > 0) {
+    EXPECT_EQ(write.length, static_cast<size_t>(buf_len));
+    data_written_ += buf_len;
+  }
   if (!write.async) {
     expected_writes_.pop();
     callback.Run(write.result);
@@ -268,13 +274,27 @@
 
 void MockServiceWorkerResponseWriter::ExpectWriteInfoOk(size_t length,
                                                         bool async) {
-  ExpectedWrite expected(true, length, async, length);
-  expected_writes_.push(expected);
+  ExpectWriteInfo(length, async, length);
 }
 
 void MockServiceWorkerResponseWriter::ExpectWriteDataOk(size_t length,
                                                         bool async) {
-  ExpectedWrite expected(false, length, async, length);
+  ExpectWriteData(length, async, length);
+}
+
+void MockServiceWorkerResponseWriter::ExpectWriteInfo(size_t length,
+                                                      bool async,
+                                                      int result) {
+  DCHECK_NE(net::ERR_IO_PENDING, result);
+  ExpectedWrite expected(true, length, async, result);
+  expected_writes_.push(expected);
+}
+
+void MockServiceWorkerResponseWriter::ExpectWriteData(size_t length,
+                                                      bool async,
+                                                      int result) {
+  DCHECK_NE(net::ERR_IO_PENDING, result);
+  ExpectedWrite expected(false, length, async, result);
   expected_writes_.push(expected);
 }
 
@@ -399,6 +419,7 @@
   EXPECT_FALSE(write_complete_);
   writer->CompletePendingWrite();
   EXPECT_TRUE(write_complete_);
+  EXPECT_EQ(net::OK, last_error_);
   EXPECT_TRUE(writer->AllExpectedWritesDone());
   EXPECT_EQ(0U, cache_writer_->bytes_written());
 }
@@ -447,6 +468,62 @@
   EXPECT_EQ(net::ERR_IO_PENDING, error);
   writer->CompletePendingWrite();
   EXPECT_TRUE(write_complete_);
+  EXPECT_EQ(net::OK, last_error_);
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, PassthroughHeadersFailSync) {
+  const size_t kHeaderSize = 16;
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  writer->ExpectWriteInfo(kHeaderSize, false, net::ERR_FAILED);
+
+  net::Error error = WriteHeaders(kHeaderSize);
+  EXPECT_EQ(net::ERR_FAILED, error);
+  EXPECT_FALSE(write_complete_);
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, PassthroughHeadersFailAsync) {
+  size_t kHeaderSize = 16;
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  writer->ExpectWriteInfo(kHeaderSize, true, net::ERR_FAILED);
+
+  net::Error error = WriteHeaders(kHeaderSize);
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  EXPECT_FALSE(write_complete_);
+  writer->CompletePendingWrite();
+  EXPECT_TRUE(write_complete_);
+  EXPECT_EQ(net::ERR_FAILED, last_error_);
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, PassthroughDataFailSync) {
+  const std::string data = "abcdef";
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  writer->ExpectWriteInfoOk(data.size(), false);
+  writer->ExpectWriteData(data.size(), false, net::ERR_FAILED);
+
+  EXPECT_EQ(net::OK, WriteHeaders(data.size()));
+  EXPECT_EQ(net::ERR_FAILED, WriteData(data));
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, PassthroughDataFailAsync) {
+  const std::string data = "abcdef";
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  writer->ExpectWriteInfoOk(data.size(), false);
+  writer->ExpectWriteData(data.size(), true, net::ERR_FAILED);
+
+  EXPECT_EQ(net::OK, WriteHeaders(data.size()));
+
+  EXPECT_EQ(net::ERR_IO_PENDING, WriteData(data));
+  writer->CompletePendingWrite();
+  EXPECT_EQ(net::ERR_FAILED, last_error_);
+  EXPECT_TRUE(write_complete_);
   EXPECT_TRUE(writer->AllExpectedWritesDone());
 }
 
@@ -489,6 +566,38 @@
   EXPECT_EQ(0U, cache_writer_->bytes_written());
 }
 
+TEST_F(ServiceWorkerCacheWriterTest, CompareHeadersFailSync) {
+  size_t response_size = 3;
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* reader = ExpectReader();
+
+  reader->ExpectReadInfo(response_size, false, net::ERR_FAILED);
+
+  EXPECT_EQ(net::ERR_FAILED, WriteHeaders(response_size));
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_TRUE(reader->AllExpectedReadsDone());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, CompareDataFailSync) {
+  const std::string data1 = "abcdef";
+  size_t response_size = data1.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* reader = ExpectReader();
+
+  reader->ExpectReadInfoOk(response_size, false);
+  reader->ExpectReadData(data1.c_str(), data1.length(), false, net::ERR_FAILED);
+
+  net::Error error = WriteHeaders(response_size);
+  EXPECT_EQ(net::OK, error);
+
+  EXPECT_EQ(net::ERR_FAILED, WriteData(data1));
+
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_TRUE(reader->AllExpectedReadsDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
 TEST_F(ServiceWorkerCacheWriterTest, CompareShortCacheReads) {
   const size_t kHeaderSize = 16;
   const std::string& data1 = "abcdef";
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 8e3752ef..141b83fb 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -76,7 +76,11 @@
       const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
     if (!context)
       return;
-    for (const auto& version_itr : context->GetLiveVersions()) {
+    // Make a copy of live versions map because StopWorker() removes the version
+    // from it when we were starting up and don't have a process yet.
+    const std::map<int64, ServiceWorkerVersion*> live_versions_copy =
+        context->GetLiveVersions();
+    for (const auto& version_itr : live_versions_copy) {
       ServiceWorkerVersion* version(version_itr.second);
       if (version->running_status() == ServiceWorkerVersion::STARTING ||
           version->running_status() == ServiceWorkerVersion::RUNNING) {
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc
index b79894e..153be6e 100644
--- a/content/browser/service_worker/service_worker_database.cc
+++ b/content/browser/service_worker/service_worker_database.cc
@@ -1025,12 +1025,12 @@
 
 ServiceWorkerDatabase::Status
 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
-  return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
-}
-
-ServiceWorkerDatabase::Status
-ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
-  return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
+  leveldb::WriteBatch batch;
+  Status status =
+      WriteResourceIdsInBatch(kUncommittedResIdKeyPrefix, ids, &batch);
+  if (status != STATUS_OK)
+    return status;
+  return WriteBatch(&batch);
 }
 
 ServiceWorkerDatabase::Status
@@ -1039,13 +1039,13 @@
 }
 
 ServiceWorkerDatabase::Status
-ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
-  return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
-}
-
-ServiceWorkerDatabase::Status
 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
-  return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
+  leveldb::WriteBatch batch;
+  Status status =
+      DeleteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
+  if (status != STATUS_OK)
+    return status;
+  return WriteBatch(&batch);
 }
 
 ServiceWorkerDatabase::Status
@@ -1404,16 +1404,6 @@
   return status;
 }
 
-ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
-    const char* id_key_prefix,
-    const std::set<int64>& ids) {
-  leveldb::WriteBatch batch;
-  Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
-  if (status != STATUS_OK)
-    return status;
-  return WriteBatch(&batch);
-}
-
 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
     const char* id_key_prefix,
     const std::set<int64>& ids,
@@ -1437,16 +1427,6 @@
   return STATUS_OK;
 }
 
-ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
-    const char* id_key_prefix,
-    const std::set<int64>& ids) {
-  leveldb::WriteBatch batch;
-  Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
-  if (status != STATUS_OK)
-    return status;
-  return WriteBatch(&batch);
-}
-
 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
     const char* id_key_prefix,
     const std::set<int64>& ids,
diff --git a/content/browser/service_worker/service_worker_database.h b/content/browser/service_worker/service_worker_database.h
index 8dd0b62..6b7dea0 100644
--- a/content/browser/service_worker/service_worker_database.h
+++ b/content/browser/service_worker/service_worker_database.h
@@ -200,39 +200,34 @@
       const std::string& user_data_name,
       std::vector<std::pair<int64, std::string>>* user_data);
 
-  // As new resources are put into the diskcache, they go into an uncommitted
-  // list. When a registration is saved that refers to those ids, they're
-  // removed from that list. When a resource no longer has any registrations or
+  // Resources should belong to one of following resource lists: uncommitted,
+  // committed and purgeable.
+  // As new resources are put into the diskcache, they go into the uncommitted
+  // list. When a registration is saved that refers to those ids, they're moved
+  // to the committed list. When a resource no longer has any registrations or
   // caches referring to it, it's added to the purgeable list. Periodically,
   // the purgeable list can be purged from the diskcache. At system startup, all
   // uncommitted ids are moved to the purgeable list.
 
-  // Reads uncommitted resource ids from the database. Returns OK on success.
+  // Reads resource ids from the uncommitted list. Returns OK on success.
   // Otherwise clears |ids| and returns an error.
   Status GetUncommittedResourceIds(std::set<int64>* ids);
 
-  // Writes |ids| into the database as uncommitted resources. Returns OK on
-  // success. Otherwise writes nothing and returns an error.
+  // Writes resource ids into the uncommitted list. Returns OK on success.
+  // Otherwise writes nothing and returns an error.
   Status WriteUncommittedResourceIds(const std::set<int64>& ids);
 
-  // Deletes uncommitted resource ids specified by |ids| from the database.
-  // Returns OK on success. Otherwise deletes nothing and returns an error.
-  Status ClearUncommittedResourceIds(const std::set<int64>& ids);
-
-  // Reads purgeable resource ids from the database. Returns OK on success.
+  // Reads resource ids from the purgeable list. Returns OK on success.
   // Otherwise clears |ids| and returns an error.
   Status GetPurgeableResourceIds(std::set<int64>* ids);
 
-  // Writes |ids| into the database as purgeable resources. Returns OK on
-  // success. Otherwise writes nothing and returns an error.
-  Status WritePurgeableResourceIds(const std::set<int64>& ids);
-
-  // Deletes purgeable resource ids specified by |ids| from the database.
-  // Returns OK on success. Otherwise deletes nothing and returns an error.
+  // Deletes resource ids from the purgeable list. Returns OK on success.
+  // Otherwise deletes nothing and returns an error.
   Status ClearPurgeableResourceIds(const std::set<int64>& ids);
 
-  // Moves |ids| from the uncommitted list to the purgeable list.
-  // Returns OK on success. Otherwise deletes nothing and returns an error.
+  // Writes resource ids into the purgeable list and removes them from the
+  // uncommitted list. Returns OK on success. Otherwise writes nothing and
+  // returns an error.
   Status PurgeUncommittedResourceIds(const std::set<int64>& ids);
 
   // Deletes all data for |origins|, namely, unique origin, registrations and
@@ -300,9 +295,6 @@
 
   // Write resource ids for |id_key_prefix| into the database. Returns OK on
   // success. Otherwise, returns writes nothing and returns an error.
-  Status WriteResourceIds(
-      const char* id_key_prefix,
-      const std::set<int64>& ids);
   Status WriteResourceIdsInBatch(
       const char* id_key_prefix,
       const std::set<int64>& ids,
@@ -311,9 +303,6 @@
   // Deletes resource ids for |id_key_prefix| from the database. Returns OK if
   // it's successfully deleted or not found in the database. Otherwise, returns
   // an error.
-  Status DeleteResourceIds(
-      const char* id_key_prefix,
-      const std::set<int64>& ids);
   Status DeleteResourceIdsInBatch(
       const char* id_key_prefix,
       const std::set<int64>& ids,
diff --git a/content/browser/service_worker/service_worker_database_unittest.cc b/content/browser/service_worker/service_worker_database_unittest.cc
index 1d19f22..b63b2ac 100644
--- a/content/browser/service_worker/service_worker_database_unittest.cc
+++ b/content/browser/service_worker/service_worker_database_unittest.cc
@@ -1396,10 +1396,10 @@
                 data.registration_id, origin, base::Time::Now()));
 }
 
-TEST(ServiceWorkerDatabaseTest, UncommittedResourceIds) {
+TEST(ServiceWorkerDatabaseTest, UncommittedAndPurgeableResourceIds) {
   scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
 
-  // Write {1, 2, 3}.
+  // Write {1, 2, 3} into the uncommitted list.
   std::set<int64> ids1;
   ids1.insert(1);
   ids1.insert(2);
@@ -1412,7 +1412,7 @@
             database->GetUncommittedResourceIds(&ids_out));
   EXPECT_EQ(ids1, ids_out);
 
-  // Write {2, 4}.
+  // Write {2, 4} into the uncommitted list.
   std::set<int64> ids2;
   ids2.insert(2);
   ids2.insert(4);
@@ -1422,63 +1422,30 @@
   ids_out.clear();
   EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
             database->GetUncommittedResourceIds(&ids_out));
-  std::set<int64> expected = base::STLSetUnion<std::set<int64> >(ids1, ids2);
+  std::set<int64> expected = base::STLSetUnion<std::set<int64>>(ids1, ids2);
   EXPECT_EQ(expected, ids_out);
 
-  // Delete {2, 3}.
-  std::set<int64> ids3;
-  ids3.insert(2);
-  ids3.insert(3);
+  // Move {2, 4} from the uncommitted list to the purgeable list.
   EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
-            database->ClearUncommittedResourceIds(ids3));
+            database->PurgeUncommittedResourceIds(ids2));
+  ids_out.clear();
+  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+            database->GetPurgeableResourceIds(&ids_out));
+  EXPECT_EQ(ids2, ids_out);
 
+  // Delete {2, 4} from the purgeable list.
+  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+            database->ClearPurgeableResourceIds(ids2));
+  ids_out.clear();
+  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
+            database->GetPurgeableResourceIds(&ids_out));
+  EXPECT_TRUE(ids_out.empty());
+
+  // {1, 3} should be still in the uncommitted list.
   ids_out.clear();
   EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
             database->GetUncommittedResourceIds(&ids_out));
-  expected = base::STLSetDifference<std::set<int64> >(expected, ids3);
-  EXPECT_EQ(expected, ids_out);
-}
-
-TEST(ServiceWorkerDatabaseTest, PurgeableResourceIds) {
-  scoped_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-
-  // Write {1, 2, 3}.
-  std::set<int64> ids1;
-  ids1.insert(1);
-  ids1.insert(2);
-  ids1.insert(3);
-  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
-            database->WritePurgeableResourceIds(ids1));
-
-  std::set<int64> ids_out;
-  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
-            database->GetPurgeableResourceIds(&ids_out));
-  EXPECT_EQ(ids1, ids_out);
-
-  // Write {2, 4}.
-  std::set<int64> ids2;
-  ids2.insert(2);
-  ids2.insert(4);
-  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
-            database->WritePurgeableResourceIds(ids2));
-
-  ids_out.clear();
-  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
-            database->GetPurgeableResourceIds(&ids_out));
-  std::set<int64> expected = base::STLSetUnion<std::set<int64> >(ids1, ids2);
-  EXPECT_EQ(expected, ids_out);
-
-  // Delete {2, 3}.
-  std::set<int64> ids3;
-  ids3.insert(2);
-  ids3.insert(3);
-  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
-            database->ClearPurgeableResourceIds(ids3));
-
-  ids_out.clear();
-  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
-            database->GetPurgeableResourceIds(&ids_out));
-  expected = base::STLSetDifference<std::set<int64> >(expected, ids3);
+  expected = base::STLSetDifference<std::set<int64>>(ids1, ids2);
   EXPECT_EQ(expected, ids_out);
 }
 
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc
index 7a960d0..95d6074 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc
@@ -40,6 +40,15 @@
     "The script resource is behind a redirect, which is disallowed.";
 const char kServiceWorkerAllowed[] = "Service-Worker-Allowed";
 
+// The net error code used when the job fails the update attempt because the new
+// script is byte-by-byte identical to the incumbent script. This error is shown
+// in DevTools and in netlog, so we want something obscure enough that it won't
+// conflict with a legitimate network error, and not too alarming if seen by
+// developers.
+// TODO(falken): Redesign this class so we don't have to fail at the network
+// stack layer just to cancel the update.
+const int kIdenticalScriptError = net::ERR_FILE_EXISTS;
+
 }  // namespace
 
 ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
@@ -170,7 +179,7 @@
   // to match the failure this function is about to return.
   if (status.status() == net::URLRequestStatus::SUCCESS &&
       *bytes_read == 0 && !cache_writer_->did_replace()) {
-    status = net::URLRequestStatus::FromError(net::ERR_FAILED);
+    status = net::URLRequestStatus::FromError(kIdenticalScriptError);
   }
 
   if (!status.is_success()) {
@@ -352,13 +361,18 @@
       info_buffer.get(),
       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
                  weak_factory_.GetWeakPtr()));
-  SetStatus(net::URLRequestStatus::FromError(error));
-  if (error != net::ERR_IO_PENDING)
-    NotifyHeadersComplete();
+  if (error == net::ERR_IO_PENDING)
+    return;
+  OnWriteHeadersComplete(error);
 }
 
 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(net::Error error) {
-  SetStatus(net::URLRequestStatus::FromError(error));
+  DCHECK_NE(net::ERR_IO_PENDING, error);
+  if (error != net::OK) {
+    NotifyStartError(net::URLRequestStatus::FromError(error));
+    return;
+  }
+  SetStatus(net::URLRequestStatus());
   NotifyHeadersComplete();
 }
 
@@ -446,7 +460,7 @@
   // ServiceWorker and write it back, but which did not replace the incumbent
   // script because the new script was identical, are considered to have failed.
   if (status.is_success() && !cache_writer_->did_replace()) {
-    reported_status = net::URLRequestStatus::FromError(net::ERR_FAILED);
+    reported_status = net::URLRequestStatus::FromError(kIdenticalScriptError);
     reported_status_message = "";
   }
 
@@ -466,8 +480,7 @@
   // exists.
   if (status.status() == net::URLRequestStatus::SUCCESS &&
       !cache_writer_->did_replace()) {
-    status =
-        net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED);
+    status = net::URLRequestStatus::FromError(kIdenticalScriptError);
     version_->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS);
   }
 
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
index 18115d9..fb5295e 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
@@ -20,7 +20,9 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
+#include "net/test/url_request/url_request_failed_job.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "net/url_request/url_request_test_job.h"
@@ -50,6 +52,7 @@
 std::string GenerateLongResponse() {
   return std::string(kNumBlocks * kBlockSize, 'a');
 }
+
 net::URLRequestJob* CreateNormalURLRequestJob(
     net::URLRequest* request,
     net::NetworkDelegate* network_delegate) {
@@ -68,6 +71,14 @@
                                     response_data, true);
 }
 
+net::URLRequestJob* CreateFailedURLRequestJob(
+    net::URLRequest* request,
+    net::NetworkDelegate* network_delegate) {
+  return new net::URLRequestFailedJob(request, network_delegate,
+                                      net::URLRequestFailedJob::START,
+                                      net::ERR_FAILED);
+}
+
 net::URLRequestJob* CreateInvalidMimeTypeJob(
     net::URLRequest* request,
     net::NetworkDelegate* network_delegate) {
@@ -393,6 +404,9 @@
     return helper_->context_wrapper();
   }
 
+  // Disables the cache to simulate cache errors.
+  void DisableCache() { context()->storage()->disk_cache()->Disable(); }
+
  protected:
   TestBrowserThreadBundle browser_thread_bundle_;
   scoped_ptr<EmbeddedWorkerTestHelper> helper_;
@@ -573,4 +587,25 @@
   EXPECT_EQ(kInvalidServiceWorkerResponseId, GetResourceId(version.get()));
 }
 
+TEST_F(ServiceWorkerWriteToCacheJobTest, Error) {
+  mock_protocol_handler_->SetCreateJobCallback(
+      base::Bind(&CreateFailedURLRequestJob));
+  request_->Start();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(net::URLRequestStatus::FAILED, request_->status().status());
+  EXPECT_EQ(net::ERR_FAILED, request_->status().error());
+  EXPECT_EQ(kInvalidServiceWorkerResponseId,
+            version_->script_cache_map()->LookupResourceId(script_url_));
+}
+
+TEST_F(ServiceWorkerWriteToCacheJobTest, FailedWriteHeadersToCache) {
+  mock_protocol_handler_->SetCreateJobCallback(
+      base::Bind(&CreateNormalURLRequestJob));
+  DisableCache();
+  request_->Start();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(net::URLRequestStatus::FAILED, request_->status().status());
+  EXPECT_EQ(net::ERR_FAILED, request_->status().error());
+}
+
 }  // namespace content
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 574f72c2..e659f72b 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -2183,6 +2183,19 @@
                   'content_shell_test_apk_run.isolate',
                 ],
               },
+              {
+                'target_name': 'content_unittests_apk_run',
+                'type': 'none',
+                'dependencies': [
+                  'content_unittests_apk',
+                ],
+                'includes': [
+                  '../build/isolate.gypi',
+                ],
+                'sources': [
+                  'content_unittests_apk.isolate',
+                ],
+              },
             ],
           },
         ],
diff --git a/content/content_unittests_apk.isolate b/content/content_unittests_apk.isolate
new file mode 100644
index 0000000..6d94fd7c
--- /dev/null
+++ b/content/content_unittests_apk.isolate
@@ -0,0 +1,27 @@
+# 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.
+{
+  'includes': [
+    '../build/android/android.isolate',
+    '../net/tools/testserver/testserver.isolate',
+    'content_unittests.isolate',
+  ],
+  'variables': {
+    'command': [
+     '<(PRODUCT_DIR)/bin/run_content_unittests',
+    ],
+    'files': [
+      '../base/base.isolate',
+      '../build/config/',
+      '../gin/v8.isolate',
+      '../third_party/angle/angle.isolate',
+      '../third_party/icu/icu.isolate',
+      '../third_party/instrumented_libraries/instrumented_libraries.isolate',
+      '../ui/gl/gl.isolate',
+      '<(PRODUCT_DIR)/bin/run_content_unittests',
+      '<(PRODUCT_DIR)/content_unittests_apk/',
+      'content_unittests.isolate',
+    ]
+  },
+}
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
index 10bf9fd6..2ebe2bb 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
@@ -48,19 +48,14 @@
     private int mLastUpdateCompositionEnd = INVALID_COMPOSITION;
 
     @VisibleForTesting
-    AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable,
-            EditorInfo outAttrs) {
+    AdapterInputConnection(
+            View view, ImeAdapter imeAdapter, Editable editable, EditorInfo outAttrs) {
         super(view, true);
         mInternalView = view;
         mImeAdapter = imeAdapter;
         mImeAdapter.setInputConnection(this);
         mEditable = editable;
 
-        // The editable passed in might have been in use by a prior keyboard and could have had
-        // prior composition spans set.  To avoid keyboard conflicts, remove all composing spans
-        // when taking ownership of an existing Editable.
-        finishComposingText();
-
         mSingleLine = true;
         outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN
                 | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
@@ -142,7 +137,6 @@
         Log.d(TAG, "Constructor called with outAttrs: %s", dumpEditorInfo(outAttrs));
 
         Selection.setSelection(mEditable, outAttrs.initialSelStart, outAttrs.initialSelEnd);
-        updateSelectionIfRequired();
     }
 
     private String dumpEditorInfo(EditorInfo editorInfo) {
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java
index 861e9bd..cfeebd9 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java
@@ -11,7 +11,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.Editable;
 import android.view.View;
-import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 
 import org.chromium.base.test.util.Feature;
@@ -39,10 +38,9 @@
         mWrapper = new TestInputMethodManagerWrapper(getActivity());
         ImeAdapterDelegate delegate = new TestImeAdapterDelegate();
         mImeAdapter = new TestImeAdapter(mWrapper, delegate);
-        EditorInfo info = new EditorInfo();
         mEditable = Editable.Factory.getInstance().newEditable("");
         mConnection = new AdapterInputConnection(
-                getContentViewCore().getContainerView(), mImeAdapter, mEditable, info);
+                getContentViewCore().getContainerView(), mImeAdapter, mEditable, new EditorInfo());
     }
 
     @SmallTest
@@ -102,54 +100,17 @@
         mWrapper.verifyUpdateSelectionCall(0, 4, 4, 0, 4);
     }
 
-    @MediumTest
-    @Feature({"TextInput", "Main"})
-    public void testNewConnectionFinishesComposingText() throws Throwable {
-        mConnection.setComposingText("abc", 1);
-        assertEquals(0, BaseInputConnection.getComposingSpanStart(mEditable));
-        assertEquals(3, BaseInputConnection.getComposingSpanEnd(mEditable));
-
-        mConnection = new AdapterInputConnection(
-                getContentViewCore().getContainerView(), mImeAdapter, mEditable, new EditorInfo());
-
-        assertEquals(1, mImeAdapter.mFinishComposingTextCounter);
-        assertEquals(-1, BaseInputConnection.getComposingSpanStart(mEditable));
-        assertEquals(-1, BaseInputConnection.getComposingSpanEnd(mEditable));
-    }
-
     private static class TestImeAdapter extends ImeAdapter {
         private final ArrayList<Integer> mKeyEventQueue = new ArrayList<Integer>();
-        private int mDeleteSurroundingTextCounter;
-        private int mFinishComposingTextCounter;
 
         public TestImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embedder) {
             super(wrapper, embedder);
         }
 
         @Override
-        public boolean deleteSurroundingText(int beforeLength, int afterLength) {
-            ++mDeleteSurroundingTextCounter;
-            return true;
-        }
-
-        @Override
         public void sendSyntheticKeyPress(int keyCode, int flags) {
             mKeyEventQueue.add(keyCode);
         }
-
-        @Override
-        protected void finishComposingText() {
-            ++mFinishComposingTextCounter;
-            super.finishComposingText();
-        }
-
-        public int getDeleteSurroundingTextCallCount() {
-            return mDeleteSurroundingTextCounter;
-        }
-
-        public Integer[] getKeyEvents() {
-            return mKeyEventQueue.toArray(new Integer[mKeyEventQueue.size()]);
-        }
     }
 
     private static class TestInputMethodManagerWrapper extends InputMethodManagerWrapper {
@@ -228,4 +189,4 @@
         assertEquals("Composition start did not match", compositionStart, actual.compositionStart);
         assertEquals("Composition end did not match", compositionEnd, actual.compositionEnd);
     }
-}
+}
\ No newline at end of file
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
index 4ad8513..bf4c10a 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -31,6 +31,7 @@
 import org.chromium.content_shell_apk.ContentShellTestBase;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -49,9 +50,9 @@
             + "</textarea>"
             + "<br/><p><span id=\"plain_text\">This is Plain Text One</span></p>"
             + "</form></body></html>");
-    private static final int COMPOSITION_KEY_CODE = 229;
 
     private TestAdapterInputConnection mConnection;
+    private TestAdapterInputConnectionFactory mConnectionFactory;
     private ImeAdapter mImeAdapter;
 
     private ContentViewCore mContentViewCore;
@@ -71,8 +72,8 @@
         mInputMethodManagerWrapper = new TestInputMethodManagerWrapper(mContentViewCore);
         getImeAdapter().setInputMethodManagerWrapper(mInputMethodManagerWrapper);
         assertEquals(0, mInputMethodManagerWrapper.getShowSoftInputCounter());
-        mContentViewCore.setAdapterInputConnectionFactory(
-                new TestAdapterInputConnectionFactory());
+        mConnectionFactory = new TestAdapterInputConnectionFactory();
+        mContentViewCore.setAdapterInputConnectionFactory(mConnectionFactory);
 
         mCallbackContainer = new TestCallbackHelperContainer(mContentViewCore);
         // TODO(aurimas) remove this wait once crbug.com/179511 is fixed.
@@ -85,21 +86,42 @@
         mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
         mImeAdapter = getImeAdapter();
 
+        // Two state updates from focus change and GestureTap.
         waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1);
+
         assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter());
         assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelStart);
         assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd);
+
+        resetUpdateStateList();
+    }
+
+    private void assertNoFurtherStateUpdate(final int index) throws InterruptedException {
+        final List<TestImeState> states = mConnectionFactory.getImeStateList();
+        assertFalse(CriteriaHelper.pollForCriteria(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return states.size() > index;
+            }
+        }));
+    }
+
+    @MediumTest
+    @Feature({"TextInput", "Main"})
+    public void testSetUpGeneratesNoFurtherStateUpdate() throws Throwable {
+        assertNoFurtherStateUpdate(0);
     }
 
     @MediumTest
     @Feature({"TextInput", "Main"})
     public void testKeyboardDismissedAfterClickingGo() throws Throwable {
         setComposingText("hello", 1);
-        waitAndVerifyStatesAndCalls(1, "hello", 5, 5, 0, 5);
+        waitAndVerifyStatesAndCalls(0, "hello", 5, 5, 0, 5);
 
         performGo(mCallbackContainer);
 
-        waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1);
         assertWaitForKeyboardStatus(false);
     }
 
@@ -108,19 +130,19 @@
     @RerunWithUpdatedContainerView
     public void testGetTextUpdatesAfterEnteringText() throws Throwable {
         setComposingText("h", 1);
-        waitAndVerifyStates(1, "h", 1, 1, 0, 1);
+        waitAndVerifyStates(0, "h", 1, 1, 0, 1);
         assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter());
 
         setComposingText("he", 1);
-        waitAndVerifyStates(2, "he", 2, 2, 0, 2);
+        waitAndVerifyStates(1, "he", 2, 2, 0, 2);
         assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter());
 
         setComposingText("hel", 1);
-        waitAndVerifyStates(3, "hel", 3, 3, 0, 3);
+        waitAndVerifyStates(2, "hel", 3, 3, 0, 3);
         assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter());
 
         commitText("hel", 1);
-        waitAndVerifyStates(4, "hel", 3, 3, -1, -1);
+        waitAndVerifyStates(3, "hel", 3, 3, -1, -1);
         assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter());
     }
 
@@ -129,10 +151,10 @@
     @RerunWithUpdatedContainerView
     public void testImeCopy() throws Exception {
         commitText("hello", 1);
-        waitAndVerifyStates(1, "hello", 5, 5, -1, -1);
+        waitAndVerifyStates(0, "hello", 5, 5, -1, -1);
 
         setSelection(2, 5);
-        waitAndVerifyStates(2, "hello", 2, 5, -1, -1);
+        waitAndVerifyStates(1, "hello", 2, 5, -1, -1);
 
         copy();
         assertClipboardContents(getActivity(), "llo");
@@ -142,7 +164,7 @@
     @Feature({"TextInput"})
     public void testEnterTextAndRefocus() throws Exception {
         commitText("hello", 1);
-        waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1);
 
         DOMUtils.clickNode(this, mContentViewCore, "input_radio");
         assertWaitForKeyboardStatus(false);
@@ -157,7 +179,7 @@
     @Feature({"TextInput"})
     public void testKeyboardNotDismissedAfterCopySelection() throws Exception {
         commitText("Sample Text", 1);
-        waitAndVerifyStatesAndCalls(1, "Sample Text", 11, 11, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, -1, -1);
         DOMUtils.clickNode(this, mContentViewCore, "input_text");
         assertWaitForKeyboardStatus(true);
         DOMUtils.longPressNode(this, mContentViewCore, "input_text");
@@ -171,7 +193,7 @@
     @Feature({"TextInput"})
     public void testImeNotDismissedAfterCutSelection() throws Exception {
         commitText("Sample Text", 1);
-        waitAndVerifyStatesAndCalls(1, "Sample Text", 11, 11, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, -1, -1);
         DOMUtils.longPressNode(this, mContentViewCore, "input_text");
         assertWaitForSelectActionBarStatus(true);
         assertWaitForKeyboardStatus(true);
@@ -206,21 +228,21 @@
     public void testLongPressInputWhileComposingText() throws Exception {
         assertWaitForSelectActionBarStatus(false);
         setComposingText("Sample Text", 1);
-        waitAndVerifyStatesAndCalls(1, "Sample Text", 11, 11, 0, 11);
+        waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, 0, 11);
         DOMUtils.longPressNode(this, mContentViewCore, "input_text");
 
         assertWaitForSelectActionBarStatus(true);
 
         // Long press will first change selection region, and then trigger IME app to show up.
         // See RenderFrameImpl::didChangeSelection() and RenderWidget::didHandleGestureEvent().
+        waitAndVerifyStatesAndCalls(1, "Sample Text", 7, 11, 0, 11);
         waitAndVerifyStatesAndCalls(2, "Sample Text", 7, 11, 0, 11);
-        waitAndVerifyStatesAndCalls(3, "Sample Text", 7, 11, 0, 11);
 
         // Now IME app wants to finish composing text because an external selection
         // change has been detected. At least Google Latin IME and Samsung IME
         // behave this way.
         finishComposingText();
-        waitAndVerifyStatesAndCalls(4, "Sample Text", 7, 11, -1, -1);
+        waitAndVerifyStatesAndCalls(3, "Sample Text", 7, 11, -1, -1);
     }
 
     @SmallTest
@@ -298,13 +320,13 @@
     @Feature({"TextInput"})
     public void testImeCut() throws Exception {
         commitText("snarful", 1);
-        waitAndVerifyStatesAndCalls(1, "snarful", 7, 7, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "snarful", 7, 7, -1, -1);
 
         setSelection(1, 5);
-        waitAndVerifyStatesAndCalls(2, "snarful", 1, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "snarful", 1, 5, -1, -1);
 
         cut();
-        waitAndVerifyStatesAndCalls(3, "sul", 1, 1, -1, -1);
+        waitAndVerifyStatesAndCalls(2, "sul", 1, 1, -1, -1);
 
         assertClipboardContents(getActivity(), "narf");
     }
@@ -323,31 +345,31 @@
         });
 
         paste();
-        waitAndVerifyStatesAndCalls(1, "blarg", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "blarg", 5, 5, -1, -1);
 
         setSelection(3, 5);
-        waitAndVerifyStatesAndCalls(2, "blarg", 3, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "blarg", 3, 5, -1, -1);
 
         paste();
         // Paste is a two step process when there is a non-zero selection.
-        waitAndVerifyStates(3, "bla", 3, 3, -1, -1);
-        waitAndVerifyStatesAndCalls(4, "blablarg", 8, 8, -1, -1);
+        waitAndVerifyStates(2, "bla", 3, 3, -1, -1);
+        waitAndVerifyStatesAndCalls(3, "blablarg", 8, 8, -1, -1);
 
         paste();
-        waitAndVerifyStatesAndCalls(5, "blablargblarg", 13, 13, -1, -1);
+        waitAndVerifyStatesAndCalls(4, "blablargblarg", 13, 13, -1, -1);
     }
 
     @SmallTest
     @Feature({"TextInput"})
     public void testImeSelectAndUnSelectAll() throws Exception {
         commitText("hello", 1);
-        waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1);
 
         selectAll();
-        waitAndVerifyStatesAndCalls(2, "hello", 0, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "hello", 0, 5, -1, -1);
 
         unselect();
-        waitAndVerifyStatesAndCalls(3, "", 0, 0, -1, -1);
+        waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1);
 
         assertWaitForKeyboardStatus(false);
     }
@@ -367,28 +389,25 @@
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testFinishComposingText() throws Throwable {
-        DOMUtils.focusNode(mWebContents, "input_radio");
-        assertWaitForKeyboardStatus(false);
-
-        focusElement("textarea");
+        focusElementAndWaitForStateUpdate("textarea");
 
         commitText("hllo", 1);
-        waitAndVerifyStatesAndCalls(1, "hllo", 4, 4, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "hllo", 4, 4, -1, -1);
 
         commitText(" ", 1);
-        waitAndVerifyStatesAndCalls(2, "hllo ", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "hllo ", 5, 5, -1, -1);
 
         setSelection(1, 1);
-        waitAndVerifyStatesAndCalls(3, "hllo ", 1, 1, -1, -1);
+        waitAndVerifyStatesAndCalls(2, "hllo ", 1, 1, -1, -1);
 
         setComposingRegion(0, 4);
-        waitAndVerifyStatesAndCalls(4, "hllo ", 1, 1, 0, 4);
+        waitAndVerifyStatesAndCalls(3, "hllo ", 1, 1, 0, 4);
 
         finishComposingText();
-        waitAndVerifyStatesAndCalls(5, "hllo ", 1, 1, -1, -1);
+        waitAndVerifyStatesAndCalls(4, "hllo ", 1, 1, -1, -1);
 
         commitText("\n", 1);
-        waitAndVerifyStatesAndCalls(6, "h\nllo ", 2, 2, -1, -1);
+        waitAndVerifyStatesAndCalls(5, "h\nllo ", 2, 2, -1, -1);
     }
 
     /*
@@ -402,18 +421,18 @@
         // The calls below are a reflection of what the stock Google Keyboard (Andr
         // when the noted key is touched on screen.
         // H
-        expectUpdateStateCall();
+        resetUpdateStateList();
         setComposingText("h", 1);
         assertUpdateStateCall(1000);
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
 
         // O
-        expectUpdateStateCall();
+        resetUpdateStateList();
         setComposingText("ho", 1);
         assertUpdateStateCall(1000);
         assertEquals("ho", mConnection.getTextBeforeCursor(9, 0));
 
-        expectUpdateStateCall();
+        resetUpdateStateList();
         setComposingText("h", 1);
         assertUpdateStateCall(1000);
         setComposingRegion(0, 1);
@@ -458,7 +477,7 @@
         // that the test reflects reality.  If this test breaks, it's possible that code has
         // changed and different calls need to be made instead.
         // "three"
-        expectUpdateStateCall();
+        resetUpdateStateList();
         setComposingText("three", 1);
         assertUpdateStateCall(1000);
         assertEquals("three", mConnection.getTextBeforeCursor(99, 0));
@@ -467,14 +486,14 @@
         commitText("three", 1);
         commitText(" ", 1);
         setComposingText("word", 1);
-        expectUpdateStateCall();
+        resetUpdateStateList();
         assertUpdateStateCall(1000);
         assertEquals("three word", mConnection.getTextBeforeCursor(99, 0));
 
         // "test"
         commitText("word", 1);
         commitText(" ", 1);
-        expectUpdateStateCall();
+        resetUpdateStateList();
         setComposingText("test", 1);
         assertUpdateStateCall(1000);
         assertEquals("three word test", mConnection.getTextBeforeCursor(99, 0));
@@ -487,19 +506,19 @@
         final String smiley = "\uD83D\uDE0A";
 
         commitText(smiley, 1);
-        waitAndVerifyStatesAndCalls(1, smiley, 2, 2, -1, -1);
+        waitAndVerifyStatesAndCalls(0, smiley, 2, 2, -1, -1);
 
         // DEL, sent via dispatchKeyEvent like it is in Android WebView or a physical keyboard.
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
 
-        waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1);
 
         // Make sure that we accept further typing after deleting the smiley.
         setComposingText("s", 1);
-        waitAndVerifyStatesAndCalls(3, "s", 1, 1, 0, 1);
+        waitAndVerifyStatesAndCalls(2, "s", 1, 1, 0, 1);
         setComposingText("sm", 1);
-        waitAndVerifyStatesAndCalls(4, "sm", 2, 2, 0, 2);
+        waitAndVerifyStatesAndCalls(3, "sm", 2, 2, 0, 2);
     }
 
     @SmallTest
@@ -508,14 +527,14 @@
         focusElement("textarea");
 
         // H
-        expectUpdateStateCall();
+        resetUpdateStateList();
         commitText("h", 1);
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
         assertUpdateStateCall(1000);
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
 
         // O
-        expectUpdateStateCall();
+        resetUpdateStateList();
         commitText("o", 1);
         assertEquals("ho", mConnection.getTextBeforeCursor(9, 0));
         assertUpdateStateCall(1000);
@@ -526,7 +545,7 @@
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
 
         // DEL
-        expectUpdateStateCall();
+        resetUpdateStateList();
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
         assertUpdateStateCall(1000);
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
@@ -538,14 +557,14 @@
         focusElement("textarea");
 
         // H
-        expectUpdateStateCall();
+        resetUpdateStateList();
         commitText("h", 1);
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
         assertUpdateStateCall(1000);
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
 
         // O
-        expectUpdateStateCall();
+        resetUpdateStateList();
         commitText("o", 1);
         assertEquals("ho", mConnection.getTextBeforeCursor(9, 0));
         assertUpdateStateCall(1000);
@@ -558,7 +577,7 @@
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
 
         // DEL
-        expectUpdateStateCall();
+        resetUpdateStateList();
         assertEquals("", mConnection.getTextBeforeCursor(9, 0));
         assertUpdateStateCall(1000);
         assertEquals("", mConnection.getTextBeforeCursor(9, 0));
@@ -567,33 +586,34 @@
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testPhysicalKeyboard() throws Throwable {
-        focusElement("textarea");
+        focusElementAndWaitForStateUpdate("textarea");
+
         // Type 'a' using a physical keyboard.
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A));
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_A));
-        waitAndVerifyStatesAndCalls(1, "a", 1, 1, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "a", 1, 1, -1, -1);
 
         // Type 'enter' key.
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
-        waitAndVerifyStatesAndCalls(2, "a\n\n", 2, 2, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "a\n\n", 2, 2, -1, -1);
 
         // Type 'b'.
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B));
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B));
-        waitAndVerifyStatesAndCalls(3, "a\nb", 3, 3, -1, -1);
+        waitAndVerifyStatesAndCalls(2, "a\nb", 3, 3, -1, -1);
     }
 
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testPhysicalKeyboard_AccentKeyCodes() throws Throwable {
-        focusElement("textarea");
+        focusElementAndWaitForStateUpdate("textarea");
 
         // h
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_H));
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_H));
         assertEquals("h", mConnection.getTextBeforeCursor(9, 0));
-        waitAndVerifyStatesAndCalls(1, "h", 1, 1, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "h", 1, 1, -1, -1);
 
         // ALT-i  (circumflex accent key on virtual keyboard)
         dispatchKeyEvent(new KeyEvent(
@@ -603,7 +623,7 @@
         dispatchKeyEvent(new KeyEvent(
                 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertEquals("hˆ", mConnection.getTextBeforeCursor(9, 0));
-        waitAndVerifyStatesAndCalls(2, "hˆ", 2, 2, 1, 2);
+        waitAndVerifyStatesAndCalls(1, "hˆ", 2, 2, 1, 2);
 
         // o
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_O));
@@ -611,7 +631,7 @@
         assertEquals("hô", mConnection.getTextBeforeCursor(9, 0));
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_O));
         assertEquals("hô", mConnection.getTextBeforeCursor(9, 0));
-        waitAndVerifyStatesAndCalls(3, "hô", 2, 2, -1, -1);
+        waitAndVerifyStatesAndCalls(2, "hô", 2, 2, -1, -1);
 
         // ALT-i
         dispatchKeyEvent(new KeyEvent(
@@ -620,7 +640,7 @@
         dispatchKeyEvent(new KeyEvent(
                 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertEquals("hôˆ", mConnection.getTextBeforeCursor(9, 0));
-        waitAndVerifyStatesAndCalls(4, "hôˆ", 3, 3, 2, 3);
+        waitAndVerifyStatesAndCalls(3, "hôˆ", 3, 3, 2, 3);
 
         // ALT-i again should have no effect
         dispatchKeyEvent(new KeyEvent(
@@ -637,8 +657,8 @@
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B));
         assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0));
         // A transitional state due to finishComposingText.
-        waitAndVerifyStates(5, "hôˆ", 3, 3, -1, -1);
-        waitAndVerifyStatesAndCalls(6, "hôˆb", 4, 4, -1, -1);
+        waitAndVerifyStates(4, "hôˆ", 3, 3, -1, -1);
+        waitAndVerifyStatesAndCalls(5, "hôˆb", 4, 4, -1, -1);
 
         // ALT-i
         dispatchKeyEvent(new KeyEvent(
@@ -647,7 +667,7 @@
         dispatchKeyEvent(new KeyEvent(
                 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON));
         assertEquals("hôˆbˆ", mConnection.getTextBeforeCursor(9, 0));
-        waitAndVerifyStatesAndCalls(7, "hôˆbˆ", 5, 5, 4, 5);
+        waitAndVerifyStatesAndCalls(6, "hôˆbˆ", 5, 5, 4, 5);
 
         // Backspace
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
@@ -656,8 +676,8 @@
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
         assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0));
         // A transitional state due to finishComposingText in deleteSurroundingTextImpl.
-        waitAndVerifyStates(8, "hôˆbˆ", 5, 5, -1, -1);
-        waitAndVerifyStatesAndCalls(9, "hôˆb", 4, 4, -1, -1);
+        waitAndVerifyStates(7, "hôˆbˆ", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(8, "hôˆb", 4, 4, -1, -1);
     }
 
     @SmallTest
@@ -674,16 +694,38 @@
     @SmallTest
     @Feature({"TextInput", "Main"})
     public void testEnterKey_AfterCommitText() throws Throwable {
-        focusElement("textarea");
+        focusElementAndWaitForStateUpdate("textarea");
 
         commitText("hello", 1);
-        waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1);
 
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
         // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
         // The second new line is not a user visible/editable one, it is a side-effect of Blink
         // using <br> internally. This only happens when \n is at the end.
+        waitAndVerifyStatesAndCalls(1, "hello\n\n", 6, 6, -1, -1);
+
+        commitText("world", 1);
+        waitAndVerifyStatesAndCalls(2, "hello\nworld", 11, 11, -1, -1);
+    }
+
+    @SmallTest
+    @Feature({"TextInput", "Main"})
+    public void testEnterKey_WhileComposingText() throws Throwable {
+        focusElementAndWaitForStateUpdate("textarea");
+
+        setComposingText("hello", 1);
+        waitAndVerifyStatesAndCalls(0, "hello", 5, 5, 0, 5);
+
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
+
+        // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
+        // A transitional state due to finishComposingText.
+        waitAndVerifyStates(1, "hello", 5, 5, -1, -1);
+        // The second new line is not a user visible/editable one, it is a side-effect of Blink
+        // using <br> internally. This only happens when \n is at the end.
         waitAndVerifyStatesAndCalls(2, "hello\n\n", 6, 6, -1, -1);
 
         commitText("world", 1);
@@ -692,33 +734,11 @@
 
     @SmallTest
     @Feature({"TextInput", "Main"})
-    public void testEnterKey_WhileComposingText() throws Throwable {
-        focusElement("textarea");
-
-        setComposingText("hello", 1);
-        waitAndVerifyStatesAndCalls(1, "hello", 5, 5, 0, 5);
-
-        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
-        dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
-
-        // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
-        // A transitional state due to finishComposingText.
-        waitAndVerifyStates(2, "hello", 5, 5, -1, -1);
-        // The second new line is not a user visible/editable one, it is a side-effect of Blink
-        // using <br> internally. This only happens when \n is at the end.
-        waitAndVerifyStatesAndCalls(3, "hello\n\n", 6, 6, -1, -1);
-
-        commitText("world", 1);
-        waitAndVerifyStatesAndCalls(4, "hello\nworld", 11, 11, -1, -1);
-    }
-
-    @SmallTest
-    @Feature({"TextInput", "Main"})
     public void testDpadKeyCodesWhileSwipingText() throws Throwable {
         focusElement("textarea");
 
         // DPAD_CENTER should cause keyboard to appear
-        expectUpdateStateCall();
+        resetUpdateStateList();
         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER));
         assertUpdateStateCall(1000);
     }
@@ -727,13 +747,13 @@
     @Feature({"TextInput"})
     public void testPastePopupShowAndHide() throws Throwable {
         commitText("hello", 1);
-        waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1);
 
         selectAll();
-        waitAndVerifyStatesAndCalls(2, "hello", 0, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(1, "hello", 0, 5, -1, -1);
 
         cut();
-        waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1);
+        waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1);
 
         DOMUtils.longPressNode(this, mContentViewCore, "input_text");
         assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
@@ -760,7 +780,7 @@
     @Feature({"TextInput"})
     public void testSelectionClearedOnKeyEvent() throws Throwable {
         commitText("hello", 1);
-        waitAndVerifyStatesAndCalls(1, "hello", 5, 5, -1, -1);
+        waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1);
 
         DOMUtils.clickNode(this, mContentViewCore, "input_text");
         assertWaitForKeyboardStatus(true);
@@ -785,6 +805,23 @@
         assertTrue(mContentViewCore.hasSelection());
     }
 
+    @MediumTest
+    @Feature({"TextInput"})
+    public void testRestartInputWhileComposingText() throws Throwable {
+        setComposingText("abc", 1);
+        waitAndVerifyStatesAndCalls(0, "abc", 3, 3, 0, 3);
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mConnection.restartInput();
+            }
+        });
+        // We don't do anything when input gets restarted. But we depend on Android's
+        // InputMethodManager and/or input methods to call finishComposingText() in setting
+        // current input connection as active or finishing the current input connection.
+        assertNoFurtherStateUpdate(1);
+    }
+
     private void performGo(TestCallbackHelperContainer testCallbackHelperContainer)
             throws Throwable {
         final AdapterInputConnection inputConnection = mConnection;
@@ -821,7 +858,7 @@
     private void waitAndVerifyStates(final int index, String text, final int selectionStart,
             final int selectionEnd, final int compositionStart, final int compositionEnd)
             throws InterruptedException {
-        final ArrayList<TestImeState> states = mConnection.mImeUpdateQueue;
+        final List<TestImeState> states = mConnectionFactory.getImeStateList();
         assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -853,14 +890,12 @@
                 }));
     }
 
-    private void expectUpdateStateCall() {
-        final TestAdapterInputConnection connection = mConnection;
-        connection.mImeUpdateQueue.clear();
+    private void resetUpdateStateList() {
+        mConnectionFactory.getImeStateList().clear();
     }
 
     private void assertUpdateStateCall(int maxms) throws Exception {
-        final TestAdapterInputConnection connection = mConnection;
-        while (connection.mImeUpdateQueue.size() == 0 && maxms > 0) {
+        while (mConnectionFactory.getImeStateList().size() == 0 && maxms > 0) {
             try {
                 Thread.sleep(50);
             } catch (Exception e) {
@@ -868,7 +903,7 @@
             }
             maxms -= 50;
         }
-        assertTrue(connection.mImeUpdateQueue.size() > 0);
+        assertTrue(mConnectionFactory.getImeStateList().size() > 0);
     }
 
     private void assertClipboardContents(final Activity activity, final String expectedContents)
@@ -1012,6 +1047,17 @@
         });
     }
 
+    /**
+     * Focus element, wait for a single state update, reset state update list.
+     * @param id ID of the element to focus.
+     */
+    private void focusElementAndWaitForStateUpdate(String id)
+            throws InterruptedException, TimeoutException {
+        focusElement(id);
+        waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1);
+        resetUpdateStateList();
+    }
+
     private void focusElement(final String id) throws InterruptedException, TimeoutException {
         DOMUtils.focusNode(mWebContents, id);
         assertWaitForKeyboardStatus(true);
@@ -1027,31 +1073,38 @@
         }));
         // When we focus another element, the connection may be recreated.
         mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
-        waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1);
     }
 
     private static class TestAdapterInputConnectionFactory extends
             ImeAdapter.AdapterInputConnectionFactory {
+        private final List<TestImeState> mImeStateList = new ArrayList<>();
+
         @Override
         public AdapterInputConnection get(View view, ImeAdapter imeAdapter,
                 Editable editable, EditorInfo outAttrs) {
-            return new TestAdapterInputConnection(view, imeAdapter, editable, outAttrs);
+            return new TestAdapterInputConnection(
+                    mImeStateList, view, imeAdapter, editable, outAttrs);
+        }
+
+        public List<TestImeState> getImeStateList() {
+            return mImeStateList;
         }
     }
 
     private static class TestAdapterInputConnection extends AdapterInputConnection {
-        private final ArrayList<TestImeState> mImeUpdateQueue = new ArrayList<TestImeState>();
+        private final List<TestImeState> mImeStateList;
 
-        public TestAdapterInputConnection(View view, ImeAdapter imeAdapter,
-                Editable editable, EditorInfo outAttrs) {
+        public TestAdapterInputConnection(List<TestImeState> imeStateList, View view,
+                ImeAdapter imeAdapter, Editable editable, EditorInfo outAttrs) {
             super(view, imeAdapter, editable, outAttrs);
+            mImeStateList = imeStateList;
         }
 
         @Override
         public void updateState(String text, int selectionStart, int selectionEnd,
                 int compositionStart, int compositionEnd, boolean requiredAck) {
-            mImeUpdateQueue.add(new TestImeState(text, selectionStart, selectionEnd,
-                    compositionStart, compositionEnd));
+            mImeStateList.add(new TestImeState(
+                    text, selectionStart, selectionEnd, compositionStart, compositionEnd));
             super.updateState(text, selectionStart, selectionEnd, compositionStart,
                     compositionEnd, requiredAck);
         }
diff --git a/content/public/browser/native_web_keyboard_event.h b/content/public/browser/native_web_keyboard_event.h
index 8c34c708..4bbb119 100644
--- a/content/public/browser/native_web_keyboard_event.h
+++ b/content/public/browser/native_web_keyboard_event.h
@@ -7,10 +7,10 @@
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "base/strings/string16.h"
 #include "build/build_config.h"
 #include "content/common/content_export.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/events/event_constants.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace ui {
@@ -46,11 +46,8 @@
 #else
   explicit NativeWebKeyboardEvent(const ui::KeyEvent& key_event);
 #if defined(USE_AURA)
-  NativeWebKeyboardEvent(ui::EventType type,
-                         bool is_char,
-                         wchar_t character,
-                         int state,
-                         double time_stamp_seconds);
+  // Create a legacy keypress event specified by |character|.
+  NativeWebKeyboardEvent(const ui::KeyEvent& key_event, base::char16 character);
 #endif
 #endif
 
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 5ab4629..b59bb46 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -66,7 +66,6 @@
 class Layer;
 }
 
-using blink::WebBeginFrameArgs;
 using blink::WebFloatPoint;
 using blink::WebRect;
 using blink::WebSelection;
@@ -859,13 +858,9 @@
 }
 
 void RenderWidgetCompositor::BeginMainFrame(const cc::BeginFrameArgs& args) {
-  double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF();
-  double deadline_sec = (args.deadline - base::TimeTicks()).InSecondsF();
-  double interval_sec = args.interval.InSecondsF();
-  WebBeginFrameArgs web_begin_frame_args =
-      WebBeginFrameArgs(frame_time_sec, deadline_sec, interval_sec);
   compositor_deps_->GetRendererScheduler()->WillBeginFrame(args);
-  widget_->webwidget()->beginFrame(web_begin_frame_args);
+  double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF();
+  widget_->webwidget()->beginFrame(frame_time_sec);
 }
 
 void RenderWidgetCompositor::BeginMainFrameNotExpectedSoon() {
diff --git a/content/renderer/gpu/render_widget_compositor_unittest.cc b/content/renderer/gpu/render_widget_compositor_unittest.cc
index f252c11..18e48a5a 100644
--- a/content/renderer/gpu/render_widget_compositor_unittest.cc
+++ b/content/renderer/gpu/render_widget_compositor_unittest.cc
@@ -27,7 +27,7 @@
 
 class MockWebWidget : public blink::WebWidget {
  public:
-  MOCK_METHOD1(beginFrame, void(const blink::WebBeginFrameArgs& args));
+  MOCK_METHOD1(beginFrame, void(double lastFrameTimeMonotonic));
 };
 
 class TestRenderWidget : public RenderWidget {
@@ -80,11 +80,7 @@
       cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
                                  interval, cc::BeginFrameArgs::NORMAL));
 
-  EXPECT_CALL(render_widget_->mock_webwidget_,
-              beginFrame(AllOf(
-                  Field(&blink::WebBeginFrameArgs::lastFrameTimeMonotonic, 1),
-                  Field(&blink::WebBeginFrameArgs::deadline, 2),
-                  Field(&blink::WebBeginFrameArgs::interval, 3))));
+  EXPECT_CALL(render_widget_->mock_webwidget_, beginFrame(1));
 
   render_widget_compositor_->BeginMainFrame(args);
 }
diff --git a/extensions/renderer/file_system_natives.cc b/extensions/renderer/file_system_natives.cc
index 67b5d13..d3b23af 100644
--- a/extensions/renderer/file_system_natives.cc
+++ b/extensions/renderer/file_system_natives.cc
@@ -11,7 +11,6 @@
 #include "storage/common/fileapi/file_system_types.h"
 #include "storage/common/fileapi/file_system_util.h"
 #include "third_party/WebKit/public/platform/WebString.h"
-#include "third_party/WebKit/public/web/WebDOMError.h"
 #include "third_party/WebKit/public/web/WebDOMFileSystem.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 
@@ -28,9 +27,6 @@
   RouteFunction("CrackIsolatedFileSystemName",
                 base::Bind(&FileSystemNatives::CrackIsolatedFileSystemName,
                            base::Unretained(this)));
-  RouteFunction(
-      "GetDOMError",
-      base::Bind(&FileSystemNatives::GetDOMError, base::Unretained(this)));
 }
 
 void FileSystemNatives::GetIsolatedFileSystem(
@@ -124,34 +120,4 @@
                                                     filesystem_id.size()));
 }
 
-void FileSystemNatives::GetDOMError(
-    const v8::FunctionCallbackInfo<v8::Value>& args) {
-  if (args.Length() != 2) {
-    NOTREACHED();
-    return;
-  }
-  if (!args[0]->IsString()) {
-    NOTREACHED();
-    return;
-  }
-  if (!args[1]->IsString()) {
-    NOTREACHED();
-    return;
-  }
-
-  std::string name(*v8::String::Utf8Value(args[0]));
-  if (name.empty()) {
-    NOTREACHED();
-    return;
-  }
-  std::string message(*v8::String::Utf8Value(args[1]));
-  // message is optional hence empty is fine.
-
-  blink::WebDOMError dom_error = blink::WebDOMError::create(
-      blink::WebString::fromUTF8(name), blink::WebString::fromUTF8(message));
-  args.GetReturnValue().Set(
-      dom_error.toV8Value(context()->v8_context()->Global(),
-                          args.GetIsolate()));
-}
-
 }  // namespace extensions
diff --git a/extensions/renderer/file_system_natives.h b/extensions/renderer/file_system_natives.h
index 8637326..ed6970c 100644
--- a/extensions/renderer/file_system_natives.h
+++ b/extensions/renderer/file_system_natives.h
@@ -21,8 +21,6 @@
   void GetIsolatedFileSystem(const v8::FunctionCallbackInfo<v8::Value>& args);
   void CrackIsolatedFileSystemName(
       const v8::FunctionCallbackInfo<v8::Value>& args);
-  // Constructs a DOMError object to be used in JavaScript.
-  void GetDOMError(const v8::FunctionCallbackInfo<v8::Value>& args);
 
   DISALLOW_COPY_AND_ASSIGN(FileSystemNatives);
 };
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc
index b6533a2..0f4fe4b 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.cc
+++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -1210,6 +1210,23 @@
           IsSignedIntegerFormat(internal_format));
 }
 
+// static
+bool GLES2Util::IsFloatFormat(uint32_t internal_format) {
+  switch (internal_format) {
+    case GL_R16F:
+    case GL_R32F:
+    case GL_RG16F:
+    case GL_RG32F:
+    case GL_R11F_G11F_B10F:
+    case GL_RGB16F:
+    case GL_RGB32F:
+    case GL_RGBA16F:
+    case GL_RGBA32F:
+      return true;
+    default:
+      return false;
+  }
+}
 
 namespace {
 
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h
index b29f96b..aea4c40 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils.h
@@ -190,6 +190,7 @@
   static bool IsUnsignedIntegerFormat(uint32_t internal_format);
   static bool IsSignedIntegerFormat(uint32_t internal_format);
   static bool IsIntegerFormat(uint32_t internal_format);
+  static bool IsFloatFormat(uint32_t internal_format);
 
   #include "../common/gles2_cmd_utils_autogen.h"
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 10550fa5..c8ffe3a 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1567,6 +1567,9 @@
   // attached. Generates GL error if not.
   bool CheckBoundReadFramebufferColorAttachment(const char* func_name);
 
+  // Infer color encoding from internalformat
+  static GLint GetColorEncodingFromInternalFormat(GLenum internalformat);
+
   // Check that the currently bound read framebuffer's color image
   // isn't the target texture of the glCopyTex{Sub}Image2D.
   bool FormsTextureCopyingFeedbackLoop(TextureRef* texture, GLint level);
@@ -4019,6 +4022,19 @@
   return true;
 }
 
+GLint GLES2DecoderImpl::GetColorEncodingFromInternalFormat(
+    GLenum internalformat) {
+  switch (internalformat) {
+    case GL_SRGB_EXT:
+    case GL_SRGB_ALPHA_EXT:
+    case GL_SRGB8:
+    case GL_SRGB8_ALPHA8:
+      return GL_SRGB;
+    default:
+      return GL_LINEAR;
+  }
+}
+
 bool GLES2DecoderImpl::FormsTextureCopyingFeedbackLoop(
     TextureRef* texture, GLint level) {
   Framebuffer* framebuffer = features().chromium_framebuffer_multisample ?
@@ -11003,6 +11019,20 @@
     return;
   }
 
+  if (feature_info_->IsES3Enabled()) {
+    GLint color_encoding = GetColorEncodingFromInternalFormat(read_format);
+    if (color_encoding != GetColorEncodingFromInternalFormat(internal_format) ||
+        GLES2Util::IsFloatFormat(internal_format) ||
+        (GLES2Util::IsSignedIntegerFormat(internal_format) !=
+         GLES2Util::IsSignedIntegerFormat(read_format)) ||
+        (GLES2Util::IsUnsignedIntegerFormat(internal_format) !=
+         GLES2Util::IsUnsignedIntegerFormat(read_format))) {
+      LOCAL_SET_GL_ERROR(
+          GL_INVALID_OPERATION, "glCopyTexImage2D", "incompatible format");
+      return;
+    }
+  }
+
   if ((channels_needed & (GLES2Util::kDepth | GLES2Util::kStencil)) != 0) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
index e1acdebe..f50e030 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
@@ -1982,6 +1982,153 @@
   EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
 }
 
+TEST_P(GLES3DecoderTest, CopyTexImage2DInvalidInternalFormat_Float) {
+  const GLuint kFBOClientTextureId = 4100;
+  const GLuint kFBOServiceTextureId = 4101;
+
+  GLenum target = GL_TEXTURE_2D;
+  GLint level = 0;
+  GLenum internal_format = GL_RG16F;
+  GLenum format = GL_RG;
+  GLenum type = GL_HALF_FLOAT;
+  GLsizei width = 16;
+  GLsizei height = 8;
+  GLint border = 0;
+
+  EXPECT_CALL(*gl_, GenTextures(_, _))
+      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
+      .RetiresOnSaturation();
+  GenHelper<GenTexturesImmediate>(kFBOClientTextureId);
+
+  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
+  DoTexImage2D(GL_TEXTURE_2D,
+               level,
+               internal_format,
+               width,
+               height,
+               0,
+               format,
+               type,
+               kSharedMemoryId,
+               kSharedMemoryOffset);
+  DoBindFramebuffer(
+      GL_READ_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
+  DoFramebufferTexture2D(GL_READ_FRAMEBUFFER,
+                         GL_COLOR_ATTACHMENT0,
+                         GL_TEXTURE_2D,
+                         kFBOClientTextureId,
+                         kFBOServiceTextureId,
+                         0,
+                         GL_NO_ERROR);
+  EXPECT_CALL(*gl_,
+              CopyTexImage2D(
+                  target, level, internal_format, 0, 0, width, height, border))
+      .Times(0);
+  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
+  CopyTexImage2D cmd;
+  cmd.Init(target, level, internal_format, 0, 0, width, height);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+}
+
+TEST_P(GLES3DecoderTest, CopyTexImage2DInvalidInternalFormat_Integer) {
+  const GLuint kFBOClientTextureId = 4100;
+  const GLuint kFBOServiceTextureId = 4101;
+
+  GLenum target = GL_TEXTURE_2D;
+  GLint level = 0;
+  GLenum internal_format = GL_RG8I;
+  GLenum format = GL_RG_INTEGER;
+  GLenum type = GL_UNSIGNED_BYTE;
+  GLsizei width = 16;
+  GLsizei height = 8;
+  GLint border = 0;
+
+  EXPECT_CALL(*gl_, GenTextures(_, _))
+      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
+      .RetiresOnSaturation();
+  GenHelper<GenTexturesImmediate>(kFBOClientTextureId);
+
+  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
+  DoTexImage2D(GL_TEXTURE_2D,
+               level,
+               GL_RG8UI,
+               width,
+               height,
+               0,
+               format,
+               type,
+               kSharedMemoryId,
+               kSharedMemoryOffset);
+  DoBindFramebuffer(
+      GL_READ_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
+  DoFramebufferTexture2D(GL_READ_FRAMEBUFFER,
+                         GL_COLOR_ATTACHMENT0,
+                         GL_TEXTURE_2D,
+                         kFBOClientTextureId,
+                         kFBOServiceTextureId,
+                         0,
+                         GL_NO_ERROR);
+  EXPECT_CALL(*gl_,
+              CopyTexImage2D(
+                  target, level, internal_format, 0, 0, width, height, border))
+      .Times(0);
+  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
+  CopyTexImage2D cmd;
+  cmd.Init(target, level, internal_format, 0, 0, width, height);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+}
+
+TEST_P(GLES3DecoderTest, CopyTexImage2DInvalidInternalFormat_sRGB) {
+  const GLuint kFBOClientTextureId = 4100;
+  const GLuint kFBOServiceTextureId = 4101;
+
+  GLenum target = GL_TEXTURE_2D;
+  GLint level = 0;
+  GLenum internal_format = GL_SRGB8;
+  GLenum format = GL_RGB;
+  GLenum type = GL_UNSIGNED_BYTE;
+  GLsizei width = 16;
+  GLsizei height = 8;
+  GLint border = 0;
+
+  EXPECT_CALL(*gl_, GenTextures(_, _))
+      .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
+      .RetiresOnSaturation();
+  GenHelper<GenTexturesImmediate>(kFBOClientTextureId);
+
+  DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
+  DoTexImage2D(GL_TEXTURE_2D,
+               level,
+               GL_RGB,
+               width,
+               height,
+               0,
+               format,
+               type,
+               kSharedMemoryId,
+               kSharedMemoryOffset);
+  DoBindFramebuffer(
+      GL_READ_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
+  DoFramebufferTexture2D(GL_READ_FRAMEBUFFER,
+                         GL_COLOR_ATTACHMENT0,
+                         GL_TEXTURE_2D,
+                         kFBOClientTextureId,
+                         kFBOServiceTextureId,
+                         0,
+                         GL_NO_ERROR);
+  EXPECT_CALL(*gl_,
+              CopyTexImage2D(
+                  target, level, internal_format, 0, 0, width, height, border))
+      .Times(0);
+  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
+  CopyTexImage2D cmd;
+  cmd.Init(target, level, internal_format, 0, 0, width, height);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+}
+
 TEST_P(GLES2DecoderManualInitTest,
        UnClearedAttachmentsGetClearedOnReadPixelsAndDrawBufferGetsRestored) {
   InitState init;
diff --git a/gpu/perftests/measurements.cc b/gpu/perftests/measurements.cc
index b2dac913305..645784d 100644
--- a/gpu/perftests/measurements.cc
+++ b/gpu/perftests/measurements.cc
@@ -57,6 +57,7 @@
   DCHECK(gpu_timing_client);
   wall_time_start_ = base::TraceTicks::Now();
   if (base::ThreadTicks::IsSupported()) {
+    base::ThreadTicks::WaitUntilInitialized();
     cpu_time_start_ = base::ThreadTicks::Now();
   } else {
     static bool logged_once = false;
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg
index cf1d7a2..0549198 100644
--- a/infra/config/cq.cfg
+++ b/infra/config/cq.cfg
@@ -44,7 +44,10 @@
       builders { name: "linux_chromium_chromeos_compile_dbg_ng" }
       builders { name: "linux_chromium_chromeos_ozone_rel_ng" }
       builders { name: "linux_chromium_chromeos_rel_ng" }
-      builders { name: "linux_chromium_clobber_rel_ng" }
+      builders {
+        name: "linux_chromium_clobber_rel_ng"
+        experiment_percentage: 100
+      }
       builders { name: "linux_chromium_compile_dbg_32_ng" }
       builders { name: "linux_chromium_compile_dbg_ng" }
       builders { name: "linux_chromium_gn_chromeos_rel" }
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index 3cac00f..982b17ac 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -191,6 +191,7 @@
 [ XP ] imported/csswg-test/css-writing-modes-3/text-orientation-script-001m.html [ WontFix ]
 [ XP ] imported/csswg-test/css-writing-modes-3/text-orientation-script-001n.html [ WontFix ]
 [ XP ] imported/csswg-test/css-writing-modes-3/text-orientation-script-001o.html [ WontFix ]
+[ XP ] imported/csswg-test/css-writing-modes-3/vertical-alignment-vrl-026.xht [ WontFix ]
 
 # Relies on precise timing which isn't reliable on XP.
 [ XP ] imported/web-platform-tests/user-timing/test_user_timing_mark.html [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 46f5c52..eccd88ac 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -61,6 +61,16 @@
 
 crbug.com/542643 fast/text/zero-font-size-2.html [ Pass Crash ]
 
+crbug.com/541598 fast/repaint/selected-replaced.html [ NeedsRebaseline ]
+crbug.com/541598 svg/custom/mouse-move-on-svg-container.xhtml [ NeedsRebaseline ]
+crbug.com/541598 svg/custom/mouse-move-on-svg-root.xhtml [ NeedsRebaseline ]
+crbug.com/541598 svg/custom/pattern-userSpaceOnUse-userToBaseTransform.xhtml [ NeedsRebaseline ]
+crbug.com/541598 svg/hixie/mixed/010.xml [ NeedsRebaseline ]
+crbug.com/541598 svg/repaint/add-background-property-on-root.html [ NeedsRebaseline ]
+crbug.com/541598 svg/repaint/change-background-color.html [ NeedsRebaseline ]
+crbug.com/541598 svg/repaint/remove-background-property-on-root.html [ NeedsRebaseline ]
+crbug.com/541598 svg/text/small-fonts-in-html5.html [ NeedsRebaseline ]
+
 crbug.com/539226 [ Win7 ] http/tests/xmlhttprequest/open-in-body-onload-sync-to-invalid-cross-origin-response-handling.html [ Timeout ]
 crbug.com/539226 [ Win7 ] http/tests/xmlhttprequest/open-in-body-onload-sync-to-invalid-redirect-response-handling.html [ Timeout ]
 crbug.com/539226 [ Win7 ] http/tests/xmlhttprequest/open-in-body-onload-sync-to-invalid-preflight-handling.html [ Timeout ]
@@ -623,8 +633,6 @@
 crbug.com/492664 imported/csswg-test/css-writing-modes-3/text-combine-upright-value-all-002.html [ ImageOnlyFailure ]
 crbug.com/492664 imported/csswg-test/css-writing-modes-3/text-combine-upright-value-all-003.html [ ImageOnlyFailure ]
 
-crbug.com/543008 [ XP ] imported/csswg-test/css-writing-modes-3/vertical-alignment-vrl-026.xht [ ImageOnlyFailure ]
-
 # These CSS Writing Modes Level 3 tests pass but causes 1px diff on images, notified to the submitter.
 crbug.com/492664 [ Mac ] imported/csswg-test/css-writing-modes-3/block-flow-direction-001.xht [ ImageOnlyFailure ]
 crbug.com/492664 [ Mac ] imported/csswg-test/css-writing-modes-3/block-flow-direction-002.xht [ ImageOnlyFailure ]
@@ -1656,6 +1664,10 @@
 crbug.com/492511 [ Mac ] fast/text/atsui-spacing-features.html [ Failure ImageOnlyFailure ]
 crbug.com/492511 [ Mac ] fast/text/international/arabic-justify.html [ Failure ImageOnlyFailure ]
 
+crbug.com/541544 fast/layers/scroll-descendant-with-cached-cliprects.html [ NeedsRebaseline ]
+crbug.com/541544 fast/repaint/create-layer-repaint.html [ NeedsRebaseline ]
+crbug.com/541544 fast/repaint/static-to-positioned.html [ NeedsRebaseline ]
+
 # Ref tests that fail due to differences in inline box structure, even though they contain the same text.
 # This happens because inline box layout uses fixed-point measurements, which can cause rounding differences.
 crbug.com/321237 [ Mac ] fast/dom/shadow/shadow-insertion-point-rendering-multiple-shadow-roots.html [ ImageOnlyFailure ]
@@ -1835,6 +1847,12 @@
 crbug.com/509025 [ Yosemite ] virtual/rootlayerscrolls/scrollbars/scrollbars-on-positioned-content.html [ ImageOnlyFailure ]
 crbug.com/509025 [ Yosemite ] virtual/rootlayerscrolls/scrollbars/short-scrollbar.html [ ImageOnlyFailure ]
 
+crbug.com/472084 editing/execCommand/5136770.html [ NeedsRebaseline ]
+crbug.com/472084 editing/execCommand/5190926.html [ NeedsRebaseline ]
+crbug.com/472084 editing/execCommand/indent-selection.html [ NeedsRebaseline ]
+crbug.com/472084 fast/backgrounds/selection-background-color-of-list-style.html [ NeedsRebaseline ]
+crbug.com/472084 fast/lists/markers-in-selection.html [ NeedsRebaseline ]
+
 crbug.com/443596 media/sources-fallback-codecs.html [ Pass Failure ]
 
 crbug.com/464736 http/tests/xmlhttprequest/ontimeout-event-override-after-failure.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/compositing/repaint/should-not-repaint-composited-descendants-on-overflow-change-expected.txt b/third_party/WebKit/LayoutTests/compositing/repaint/should-not-repaint-composited-descendants-on-overflow-change-expected.txt
index 4fa25150..2e2b15a 100644
--- a/third_party/WebKit/LayoutTests/compositing/repaint/should-not-repaint-composited-descendants-on-overflow-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/repaint/should-not-repaint-composited-descendants-on-overflow-change-expected.txt
@@ -7,7 +7,6 @@
       "drawsContent": true,
       "repaintRects": [
         [0, 200, 200, 200],
-        [0, 200, 200, 200],
         [0, 0, 200, 200]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/css3/flexbox/repaint-opacity-change-expected.txt b/third_party/WebKit/LayoutTests/css3/flexbox/repaint-opacity-change-expected.txt
index 90a384b..d89acd27 100644
--- a/third_party/WebKit/LayoutTests/css3/flexbox/repaint-opacity-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/flexbox/repaint-opacity-change-expected.txt
@@ -7,7 +7,6 @@
       "drawsContent": true,
       "repaintRects": [
         [18, 18, 764, 20],
-        [18, 18, 764, 20],
         [18, 18, 764, 20]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/copy-cut-paste-supported.html b/third_party/WebKit/LayoutTests/editing/pasteboard/copy-cut-paste-supported.html
new file mode 100644
index 0000000..dc1f15b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/pasteboard/copy-cut-paste-supported.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+test(function() {
+  assert_true(document.queryCommandSupported('copy'));
+  assert_true(document.queryCommandSupported('cut'));
+  assert_true(document.queryCommandSupported('paste'));
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-z-index-change-repaint-expected.txt b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-z-index-change-repaint-expected.txt
index c5cd006..8b7c25e 100644
--- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-z-index-change-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-z-index-change-repaint-expected.txt
@@ -7,7 +7,6 @@
       "drawsContent": true,
       "repaintRects": [
         [8, 236, 200, 100],
-        [8, 236, 200, 100],
         [8, 126, 200, 100]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-transform-value.html b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-transform-value.html
new file mode 100644
index 0000000..c7c6746b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-transform-value.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<style>
+div {
+  height: 200px;
+  width: 200px;
+  position: absolute;
+}
+</style>
+
+<div id="box1" style="left: 100px; top: 200px; transform: scale(1.5, 0.75)"></div>
+<div id="box2" style="left: 400px; top: 200px; transform: rotate(30deg)"></div>
+<div id="box3" style="left: 100px; top: 400px; transform: translate(50px, 80px)"></div>
+<div id="box4" style="left: 400px; top: 400px; transform: translate(10px, 50px) scale(0.8) rotate(-10deg)"></div>
+
+<script>
+test(function() {
+  assert_equals(getComputedStyle(box1).webkitTransform, "matrix(1.5, 0, 0, 0.75, 0, 0)");
+  assert_equals(getComputedStyle(box2).webkitTransform, "matrix(0.866025, 0.5, -0.5, 0.866025, 0, 0)");
+  assert_equals(getComputedStyle(box3).webkitTransform, "matrix(1, 0, 0, 1, 50, 80)");
+  assert_equals(getComputedStyle(box4).webkitTransform, "matrix(0.787846, -0.138919, 0.138919, 0.787846, 10, 50)");
+}, "Testing getComputedStyle(element).webkitTransform");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all-expected.html b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all-expected.html
index dcf8e0c8..3eeb465 100644
--- a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all-expected.html
+++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all-expected.html
@@ -15,4 +15,5 @@
   <p>MM0<br>0===
   <p>M 0<br>0===
   <p lang="ja">&#x6F22;&#x6F22;&#x6F22;<br>&#x6F22;&#x3002;
+  <p>=======
 </div>
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all.html b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all.html
index 2a2f52c..05223d1 100644
--- a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all.html
+++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/css3-word-break-break-all.html
@@ -30,4 +30,5 @@
   <p>MM00===
   <p>M 00===
   <p lang="ja">&#x6F22;&#x6F22;&#x6F22;&#x6F22;&#x3002;
+  <p>=======
 </div>
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl-expected.txt b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl-expected.txt
new file mode 100644
index 0000000..78a2d15
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl-expected.txt
@@ -0,0 +1,4 @@
+There should be only one line of arabic below.
+
+اب و عدم وابستگی.
+PASS
diff --git a/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl.html b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl.html
new file mode 100644
index 0000000..c3ed15d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css3-text/css3-word-break/word-break-all-rtl.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+div {
+    direction: rtl;
+    line-height: 20px;
+    border: 1px solid black;
+    display: inline-block;
+}
+</style>
+<p>There should be only one line of arabic below.</p>
+<div id="test" style="word-break: break-all" data-expected-height=22>اب و عدم وابستگی.</div>
+<script src="../../../resources/check-layout.js"></script>
+<script>
+    checkLayout("#test");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/select/menulist-remove-option-onchange.html b/third_party/WebKit/LayoutTests/fast/forms/select/menulist-remove-option-onchange.html
index d10dff1..4110f2d 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/select/menulist-remove-option-onchange.html
+++ b/third_party/WebKit/LayoutTests/fast/forms/select/menulist-remove-option-onchange.html
@@ -8,6 +8,12 @@
 <option>b</option>
 <option>c</option>
 </select>
+<select id="select2">
+<option value="">Placeholder</option>
+<option>a</option>
+<option>b</option>
+<option>c</option>
+</select>
 <script>
 var select = document.querySelector('select');
 var placeholderOption = document.querySelector('option');
@@ -32,5 +38,32 @@
     eventSender.keyDown('c');
     assert_equals(selectedValue, 'c');
 }, 'Change event should be dispatched after the option removal.');
+
+selectedValue = null;
+var select2 = document.querySelector('#select2');
+select2.focus();
+select2.addEventListener('change', function() {
+    selectedValue = select2.value;
+    if (select2.options[0].value == '') {
+        var options = select2.options;
+        for (var i = 0; i < options.length - 1; ++i) {
+            options[i].label = options[i + 1].label;
+            options[i].value = options[i + 1].value;
+            options[i].textContent = options[i + 1].textContent;
+        }
+        select2.removeChild(options[options.length - 1]);
+        select2.value = selectedValue;
+    }
+});
+test(function() {
+    eventSender.keyDown('a');
+    assert_equals(select2.selectedIndex, 0);
+    assert_equals(selectedValue, 'a');
+    internals.resetTypeAheadSession(select2);
+
+    eventSender.keyDown('b');
+    assert_equals(select2.selectedIndex, 1);
+    assert_equals(selectedValue, 'b');
+}, 'Change event should be dispatched after updating selection programatically.');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/change-transform-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/change-transform-expected.txt
index c9ee0ff3..3d5fe310 100644
--- a/third_party/WebKit/LayoutTests/fast/repaint/change-transform-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/repaint/change-transform-expected.txt
@@ -7,7 +7,6 @@
       "drawsContent": true,
       "repaintRects": [
         [35, 35, 50, 50],
-        [10, 10, 100, 100],
         [10, 10, 100, 100]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/erase-overflow-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/erase-overflow-expected.txt
index 00a5f7db..032f086 100644
--- a/third_party/WebKit/LayoutTests/fast/repaint/erase-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/repaint/erase-overflow-expected.txt
@@ -8,7 +8,6 @@
       "repaintRects": [
         [8, 108, 100, 100],
         [8, 8, 100, 200],
-        [8, 8, 100, 200],
         [8, 8, 100, 100]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/mix-blend-mode-separate-stacking-context-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/mix-blend-mode-separate-stacking-context-expected.txt
index 8837d253..dc21d3b 100644
--- a/third_party/WebKit/LayoutTests/fast/repaint/mix-blend-mode-separate-stacking-context-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/repaint/mix-blend-mode-separate-stacking-context-expected.txt
@@ -8,7 +8,6 @@
       "repaintRects": [
         [348, 48, 60, 60],
         [328, 28, 60, 60],
-        [328, 28, 60, 60],
         [248, 48, 60, 60],
         [188, 28, 20, 80],
         [148, 48, 60, 60],
@@ -16,7 +15,6 @@
         [128, 88, 80, 20],
         [88, 28, 20, 80],
         [48, 48, 60, 60],
-        [48, 48, 60, 60],
         [28, 88, 80, 20]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/paint-invalidation-with-opacity-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/paint-invalidation-with-opacity-expected.txt
index af3f2ac7..7596237 100644
--- a/third_party/WebKit/LayoutTests/fast/repaint/paint-invalidation-with-opacity-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/repaint/paint-invalidation-with-opacity-expected.txt
@@ -6,7 +6,6 @@
       "contentsOpaque": true,
       "drawsContent": true,
       "repaintRects": [
-        [8, 8, 100, 100],
         [8, 8, 100, 100]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt
new file mode 100644
index 0000000..7b8f437
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt
@@ -0,0 +1,57 @@
+{
+  "bounds": [800, 600],
+  "children": [
+    {
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "repaintRects": [
+        [1, 1, 100, 100],
+        [1, 1, 100, 100]
+      ],
+      "paintInvalidationClients": [
+        "LayoutBlockFlow DIV id='target'",
+        "LayoutBlockFlow DIV id='child'"
+      ],
+      "children": [
+        {
+          "position": [8, 8]
+        },
+        {
+          "shouldFlattenTransform": false,
+          "children": [
+            {
+              "position": [8, 8],
+              "bounds": [784, 52],
+              "drawsContent": true,
+              "repaintRects": [
+                [1, 1, 100, 100],
+                [1, 1, 100, 100],
+                [0, 52, 784, 49]
+              ],
+              "paintInvalidationClients": [
+                "LayoutBlockFlow DIV id='target'",
+                "LayoutBlockFlow DIV id='child'",
+                "LayoutBlockFlow (relative positioned) DIV"
+              ]
+            },
+            {
+              "position": [9, 9],
+              "bounds": [100, 100],
+              "drawsContent": true,
+              "repaintRects": [
+                [0, 0, 100, 100],
+                [0, 0, 100, 100]
+              ],
+              "paintInvalidationClients": [
+                "LayoutBlockFlow DIV id='target'",
+                "LayoutBlockFlow DIV id='child'"
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer.html b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer.html
new file mode 100644
index 0000000..69ca613
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-descendants-when-receiving-paint-layer.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="../../fast/repaint/resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  target.style.opacity = 0.2;
+}
+onload = runRepaintTest;
+</script>
+<!--Tests paint invalidation on a non-stacking-context squashing layer when a sublayer is created
+and the descendants of the sublayer changes paint invalidation container. Passes if we invalidate
+the descendants of the layer on the squashing layer.-->
+<div style="will-change: transform; position: fixed"></div>
+<div style="position: relative; border: 1px solid black;">  
+  <div id="target" style="opacity: 1; height: 50px; width: 50px;">
+    <div id="child" style="width: 100px; height: 100px"></div>
+  </div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/layers/scroll-descendant-with-cached-cliprects-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/layers/scroll-descendant-with-cached-cliprects-expected.txt
index c41e0bb..5165e030 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/layers/scroll-descendant-with-cached-cliprects-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/layers/scroll-descendant-with-cached-cliprects-expected.txt
@@ -13,8 +13,7 @@
         [677, 52, 100, 100],
         [677, 52, 100, 100],
         [677, 52, 100, 100],
-        [677, 52, 100, 100],
-        [0, 0, 100, 100]
+        [677, 52, 100, 100]
       ],
       "paintInvalidationClients": [
         "LayoutBlockFlow (positioned) DIV id='scrollpanel'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/create-layer-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/create-layer-repaint-expected.txt
index 2d60bf0..4045257 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/create-layer-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/create-layer-repaint-expected.txt
@@ -7,7 +7,6 @@
       "drawsContent": true,
       "repaintRects": [
         [28, 56, 500, 50],
-        [28, 56, 500, 50],
         [28, 56, 250, 50]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/static-to-positioned-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/static-to-positioned-expected.txt
index 998e1428..b9472ba 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/static-to-positioned-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/static-to-positioned-expected.txt
@@ -8,9 +8,7 @@
       "repaintRects": [
         [8, 332, 100, 100],
         [8, 282, 100, 100],
-        [8, 282, 100, 100],
         [8, 132, 100, 100],
-        [8, 82, 100, 100],
         [8, 82, 100, 100]
       ],
       "paintInvalidationClients": [
diff --git a/third_party/WebKit/LayoutTests/virtual/spv2/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt b/third_party/WebKit/LayoutTests/virtual/spv2/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt
new file mode 100644
index 0000000..1b0274b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/spv2/paint/invalidation/invalidate-descendants-when-receiving-paint-layer-expected.txt
@@ -0,0 +1,22 @@
+{
+  "bounds": [800, 600],
+  "children": [
+    {
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "repaintRects": [
+        [9, 9, 100, 100],
+        [9, 9, 100, 100],
+        [8, 8, 784, 101],
+        [8, 8, 784, 52]
+      ],
+      "paintInvalidationClients": [
+        "LayoutBlockFlow (relative positioned) DIV",
+        "LayoutBlockFlow DIV id='target'",
+        "LayoutBlockFlow DIV id='child'"
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/ManualTests/computed-transform-value.html b/third_party/WebKit/ManualTests/computed-transform-value.html
deleted file mode 100644
index cea435c..0000000
--- a/third_party/WebKit/ManualTests/computed-transform-value.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-   "http://www.w3.org/TR/html4/loose.dtd">
-
-<html lang="en">
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-    <title>Get Computed Transform</title>
-    <style type="text/css" media="screen">
-    .box {
-        height: 200px;
-        width: 200px;
-        background-color: #00a0a0;
-    }
-    </style>
-    <script type="text/javascript" charset="utf-8">
-        function printTransform(event)
-        {
-            var box = event.target;
-            var computedTransform = window.getComputedStyle(box).webkitTransform;
-            document.getElementById("output").innerHTML = 'Computed transform is ' + computedTransform;
-        }
-        
-    </script>
-</head>
-<body onclick="printTransform(event)">
-
-<h1>Testing transform computed style</h1>
-<p>All boxes are 200x200 pixels. When you click on an element, the computed transform style will be displayed below</p>
-<p id="output">Computed style for transform displayed here</p>
-
-
-<div class="box" style="position: absolute; left: 100px; top: 200px; transform: scale(1.5, 0.75)">
-  scale(1.5, 0.75)
-</div>
-
-<div class="box" style="position: absolute; left: 400px; top: 200px; transform: rotate(30deg)">
-  rotate(30deg)
-</div>
-
-<div class="box" style="position: absolute; left: 100px; top: 400px; transform: translate(50px, 80px)">
-  translate(50px, 80px)
-</div>
-
-<div class="box" style="position: absolute; left: 400px; top: 400px; transform: translate(10px, 50px) scale(0.8) rotate(-10deg)">
-  translate(10px, 50px) scale(0.8) rotate(-10deg)
-</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/ManualTests/scroll-after-layer-removal.html b/third_party/WebKit/ManualTests/scroll-after-layer-removal.html
new file mode 100644
index 0000000..9ba5dfbe
--- /dev/null
+++ b/third_party/WebKit/ManualTests/scroll-after-layer-removal.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<style>
+#scroll {
+    width: 100px;
+    height: 100px;
+    overflow: scroll;
+}
+.trans { will-change: transform; }
+#space { height: 1000px; }
+</style>
+<p>This test verifies that cc does not intercept scroll events after a scroller is un-promoted.</p>
+<p>It passes if the box can be scrolled with the mouse wheel.</p>
+<p>On high DPI, pass --disable-prefer-compositing-to-lcd-text for a meaningful test.</p>
+<div id="scroll" class="trans"><div id="space"></div>
+</div>
+<script>
+setTimeout(function() { document.querySelector('#scroll').classList.remove('trans'); }, 1000);
+</script>
diff --git a/third_party/WebKit/Source/core/css/CSSStyleRule.cpp b/third_party/WebKit/Source/core/css/CSSStyleRule.cpp
index 7b3d39c..7f638c6 100644
--- a/third_party/WebKit/Source/core/css/CSSStyleRule.cpp
+++ b/third_party/WebKit/Source/core/css/CSSStyleRule.cpp
@@ -32,7 +32,8 @@
 
 namespace blink {
 
-using SelectorTextCache = HashMap<const CSSStyleRule*, String>;
+using SelectorTextCache = WillBePersistentHeapHashMap<RawPtrWillBeWeakMember<const CSSStyleRule>, String>;
+
 static SelectorTextCache& selectorTextCache()
 {
     DEFINE_STATIC_LOCAL(SelectorTextCache, cache, ());
@@ -52,7 +53,9 @@
         m_propertiesCSSOMWrapper->clearParentRule();
 #endif
     if (hasCachedSelectorText()) {
+#if !ENABLE(OILPAN)
         selectorTextCache().remove(this);
+#endif
         setHasCachedSelectorText(false);
     }
 }
diff --git a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
index 0675191..29125bd 100644
--- a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
@@ -79,6 +79,8 @@
     TriState (*state)(LocalFrame&, Event*);
     String (*value)(LocalFrame&, Event*);
     bool isTextInsertion;
+    // TODO(yosin) We should have |canExecute()|, which checks clipboard
+    // accessibility to simplify |Editor::Command::execute()|.
     bool allowExecutionWhenDisabled;
 };
 
@@ -295,8 +297,24 @@
     return executeApplyStyle(frame, source, EditActionSetBackgroundColor, CSSPropertyBackgroundColor, value);
 }
 
-static bool executeCopy(LocalFrame& frame, Event*, EditorCommandSource, const String&)
+static bool canWriteClipboard(LocalFrame& frame, EditorCommandSource source)
 {
+    if (source == CommandFromMenuOrKeyBinding)
+        return true;
+    Settings* settings = frame.settings();
+    bool defaultValue = (settings && settings->javaScriptCanAccessClipboard()) || UserGestureIndicator::processingUserGesture();
+    return frame.editor().client().canCopyCut(&frame, defaultValue);
+}
+
+static bool executeCopy(LocalFrame& frame, Event*, EditorCommandSource source, const String&)
+{
+    // To support |allowExecutionWhenDisabled|, we need to check clipboard
+    // accessibility here rather than |Editor::Command::execute()|.
+    // TODO(yosin) We should move checking |canWriteClipboard()| to
+    // |Editor::Command::execute()| with introducing appropriate predicate, e.g.
+    // |canExecute()|. See also "Cut", and "Paste" command.
+    if (!canWriteClipboard(frame, source))
+        return false;
     frame.editor().copy();
     return true;
 }
@@ -310,8 +328,15 @@
     return true;
 }
 
-static bool executeCut(LocalFrame& frame, Event*, EditorCommandSource, const String&)
+static bool executeCut(LocalFrame& frame, Event*, EditorCommandSource source, const String&)
 {
+    // To support |allowExecutionWhenDisabled|, we need to check clipboard
+    // accessibility here rather than |Editor::Command::execute()|.
+    // TODO(yosin) We should move checking |canWriteClipboard()| to
+    // |Editor::Command::execute()| with introducing appropriate predicate, e.g.
+    // |canExecute()|. See also "Copy", and "Paste" command.
+    if (!canWriteClipboard(frame, source))
+        return false;
     frame.editor().cut();
     return true;
 }
@@ -941,14 +966,37 @@
     return true;
 }
 
-static bool executePaste(LocalFrame& frame, Event*, EditorCommandSource, const String&)
+static bool canReadClipboard(LocalFrame& frame, EditorCommandSource source)
 {
+    if (source == CommandFromMenuOrKeyBinding)
+        return true;
+    Settings* settings = frame.settings();
+    bool defaultValue = settings && settings->javaScriptCanAccessClipboard() && settings->DOMPasteAllowed();
+    return frame.editor().client().canPaste(&frame, defaultValue);
+}
+
+static bool executePaste(LocalFrame& frame, Event*, EditorCommandSource source, const String&)
+{
+    // To support |allowExecutionWhenDisabled|, we need to check clipboard
+    // accessibility here rather than |Editor::Command::execute()|.
+    // TODO(yosin) We should move checking |canReadClipboard()| to
+    // |Editor::Command::execute()| with introducing appropriate predicate, e.g.
+    // |canExecute()|. See also "Copy", and "Cut" command.
+    if (!canReadClipboard(frame, source))
+        return false;
     frame.editor().paste();
     return true;
 }
 
 static bool executePasteGlobalSelection(LocalFrame& frame, Event*, EditorCommandSource source, const String&)
 {
+    // To support |allowExecutionWhenDisabled|, we need to check clipboard
+    // accessibility here rather than |Editor::Command::execute()|.
+    // TODO(yosin) We should move checking |canReadClipboard()| to
+    // |Editor::Command::execute()| with introducing appropriate predicate, e.g.
+    // |canExecute()|. See also "Copy", and "Cut" command.
+    if (!canReadClipboard(frame, source))
+        return false;
     if (!frame.editor().behavior().supportsGlobalSelection())
         return false;
     ASSERT_UNUSED(source, source == CommandFromMenuOrKeyBinding);
@@ -1170,26 +1218,6 @@
     return false;
 }
 
-static bool supportedCopyCut(LocalFrame* frame)
-{
-    if (!frame)
-        return false;
-
-    Settings* settings = frame->settings();
-    bool defaultValue = (settings && settings->javaScriptCanAccessClipboard()) || UserGestureIndicator::processingUserGesture();
-    return frame->editor().client().canCopyCut(frame, defaultValue);
-}
-
-static bool supportedPaste(LocalFrame* frame)
-{
-    if (!frame)
-        return false;
-
-    Settings* settings = frame->settings();
-    bool defaultValue = settings && settings->javaScriptCanAccessClipboard() && settings->DOMPasteAllowed();
-    return frame->editor().client().canPaste(frame, defaultValue);
-}
-
 // Enabled functions
 
 static bool enabled(LocalFrame&, Event*, EditorCommandSource)
@@ -1230,13 +1258,17 @@
     return selection.isCaret() && selection.isContentEditable();
 }
 
-static bool enabledCopy(LocalFrame& frame, Event*, EditorCommandSource)
+static bool enabledCopy(LocalFrame& frame, Event*, EditorCommandSource source)
 {
+    if (!canWriteClipboard(frame, source))
+        return false;
     return frame.editor().canDHTMLCopy() || frame.editor().canCopy();
 }
 
-static bool enabledCut(LocalFrame& frame, Event*, EditorCommandSource)
+static bool enabledCut(LocalFrame& frame, Event*, EditorCommandSource source)
 {
+    if (!canWriteClipboard(frame, source))
+        return false;
     return frame.editor().canDHTMLCut() || frame.editor().canCut();
 }
 
@@ -1270,8 +1302,10 @@
     return frame.selection().isCaretOrRange() && frame.selection().isContentRichlyEditable() && frame.selection().rootEditableElement();
 }
 
-static bool enabledPaste(LocalFrame& frame, Event*, EditorCommandSource)
+static bool enabledPaste(LocalFrame& frame, Event*, EditorCommandSource source)
 {
+    if (!canReadClipboard(frame, source))
+        return false;
     return frame.editor().canPaste();
 }
 
@@ -1457,9 +1491,9 @@
         { "BackColor", {4, executeBackColor, supported, enabledInRichlyEditableText, stateNone, valueBackColor, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "BackwardDelete", {5, executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, // FIXME: remove BackwardDelete when Safari for Windows stops using it.
         { "Bold", {6, executeToggleBold, supported, enabledInRichlyEditableText, stateBold, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
-        { "Copy", {7, executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
+        { "Copy", {7, executeCopy, supported, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
         { "CreateLink", {8, executeCreateLink, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
-        { "Cut", {9, executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
+        { "Cut", {9, executeCut, supported, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
         { "DefaultParagraphSeparator", {10, executeDefaultParagraphSeparator, supported, enabled, stateNone, valueDefaultParagraphSeparator, notTextInsertion, doNotAllowExecutionWhenDisabled} },
         { "Delete", {11, executeDelete, supported, enabledDelete, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "DeleteBackward", {12, executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
@@ -1553,8 +1587,8 @@
         { "MoveWordRightAndModifySelection", {100, executeMoveWordRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "Outdent", {101, executeOutdent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "OverWrite", {102, executeToggleOverwrite, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
-        { "Paste", {103, executePaste, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
-        { "PasteAndMatchStyle", {104, executePasteAndMatchStyle, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
+        { "Paste", {103, executePaste, supported, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
+        { "PasteAndMatchStyle", {104, executePasteAndMatchStyle, supported, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
         { "PasteGlobalSelection", {105, executePasteGlobalSelection, supportedFromMenuOrKeyBinding, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
         { "Print", {106, executePrint, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "Redo", {107, executeRedo, supported, enabledRedo, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
@@ -1731,6 +1765,10 @@
 
 bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) const
 {
+    // TODO(yosin) We should move this logic into |canExecute()| member function
+    // in |EditorInternalCommand| to replace |allowExecutionWhenDisabled|.
+    // |allowExecutionWhenDisabled| is for "Copy", "Cut" and "Paste" commands
+    // only.
     if (!isEnabled(triggeringEvent)) {
         // Let certain commands be executed when performed explicitly even if they are disabled.
         if (!isSupported() || !m_frame || !m_command->allowExecutionWhenDisabled)
diff --git a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp
index 8c30986..79bfdbe1 100644
--- a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp
+++ b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp
@@ -235,7 +235,7 @@
 
 void EventHandlerRegistry::clearWeakMembers(Visitor* visitor)
 {
-    Vector<EventTarget*> deadTargets;
+    Vector<RawPtrWillBeUntracedMember<EventTarget>> deadTargets;
     for (size_t i = 0; i < EventHandlerClassCount; ++i) {
         EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
         const EventTargetSet* targets = &m_targets[handlerClass];
@@ -258,7 +258,7 @@
     // Remove all event targets under the detached document.
     for (size_t handlerClassIndex = 0; handlerClassIndex < EventHandlerClassCount; ++handlerClassIndex) {
         EventHandlerClass handlerClass = static_cast<EventHandlerClass>(handlerClassIndex);
-        Vector<EventTarget*> targetsToRemove;
+        Vector<RawPtrWillBeUntracedMember<EventTarget>> targetsToRemove;
         const EventTargetSet* targets = &m_targets[handlerClass];
         for (const auto& eventTarget : *targets) {
             if (Node* node = eventTarget.key->toNode()) {
diff --git a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h
index b89ea927..987eecf 100644
--- a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h
+++ b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h
@@ -14,7 +14,7 @@
 class Document;
 class EventTarget;
 
-typedef HashCountedSet<EventTarget*> EventTargetSet;
+typedef HashCountedSet<RawPtrWillBeUntracedMember<EventTarget>> EventTargetSet;
 
 // Registry for keeping track of event handlers. Note that only handlers on
 // documents that can be rendered or can receive input (i.e., are attached to a
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index c0b3b8d..41409ff 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -821,7 +821,7 @@
 
 void LocalFrame::clearWeakMembers(Visitor* visitor)
 {
-    Vector<HTMLPlugInElement*> deadPlugins;
+    Vector<UntracedMember<HTMLPlugInElement>> deadPlugins;
     for (const auto& pluginElement : m_pluginElements) {
         if (!Heap::isHeapObjectAlive(pluginElement)) {
             pluginElement->shouldDisposePlugin();
diff --git a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
index 02debc6..e6cdcd7 100644
--- a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
@@ -278,14 +278,13 @@
     setSuggestedIndex(-1);
     if (m_isAutofilledByPreview)
         setAutofilled(false);
-    setSelectedIndex(optionIndex);
+    SelectOptionFlags flags = DeselectOtherOptions;
+    if (sendEvents)
+        flags |= DispatchInputAndChangeEvent | UserDriven;
+    selectOption(optionIndex, flags);
 
-    if (sendEvents && previousSelectedIndex != selectedIndex()) {
-        if (usesMenuList())
-            dispatchInputAndChangeEventForMenuList(false);
-        else
-            listBoxOnChange();
-    }
+    if (sendEvents && previousSelectedIndex != selectedIndex() && !usesMenuList())
+        listBoxOnChange();
 }
 
 String HTMLSelectElement::suggestedValue() const
@@ -717,12 +716,12 @@
     }
 }
 
-void HTMLSelectElement::dispatchInputAndChangeEventForMenuList(bool requiresUserGesture)
+void HTMLSelectElement::dispatchInputAndChangeEventForMenuList()
 {
     ASSERT(usesMenuList());
 
     HTMLOptionElement* selectedOption = this->selectedOption();
-    if (m_lastOnChangeOption.get() != selectedOption && (!requiresUserGesture || m_isProcessingUserDrivenChange)) {
+    if (m_lastOnChangeOption.get() != selectedOption && m_isProcessingUserDrivenChange) {
         m_lastOnChangeOption = selectedOption;
         m_isProcessingUserDrivenChange = false;
         RefPtrWillBeRawPtr<HTMLSelectElement> protector(this);
@@ -971,17 +970,17 @@
     if (selectedIndex() != optionIndex && isAutofilled())
         setAutofilled(false);
 
-    HTMLElement* element = 0;
+    HTMLOptionElement* element = nullptr;
     if (listIndex >= 0) {
-        element = items[listIndex];
-        if (isHTMLOptionElement(*element)) {
-            // setActiveSelectionAnchorIndex is O(N).
-            if (m_activeSelectionAnchorIndex < 0 || shouldDeselect)
-                setActiveSelectionAnchorIndex(listIndex);
-            if (m_activeSelectionEndIndex < 0 || shouldDeselect)
-                setActiveSelectionEndIndex(listIndex);
-            toHTMLOptionElement(*element).setSelectedState(true);
-        }
+        // listIndex must point an HTMLOptionElement if listIndex is not -1
+        // because optionToListIndex() returned it.
+        element = toHTMLOptionElement(items[listIndex]);
+        // setActiveSelectionAnchorIndex is O(N).
+        if (m_activeSelectionAnchorIndex < 0 || shouldDeselect)
+            setActiveSelectionAnchorIndex(listIndex);
+        if (m_activeSelectionEndIndex < 0 || shouldDeselect)
+            setActiveSelectionEndIndex(listIndex);
+        element->setSelectedState(true);
     }
 
     // deselectItemsWithoutValidation() is O(N).
@@ -1002,6 +1001,8 @@
         m_isProcessingUserDrivenChange = flags & UserDriven;
         if (flags & DispatchInputAndChangeEvent)
             dispatchInputAndChangeEventForMenuList();
+        else
+            m_lastOnChangeOption = element;
         if (LayoutObject* layoutObject = this->layoutObject()) {
             if (usesMenuList()) {
                 // didSetSelectedIndex() is O(N) because of listToOptionIndex
diff --git a/third_party/WebKit/Source/core/html/HTMLSelectElement.h b/third_party/WebKit/Source/core/html/HTMLSelectElement.h
index 0f8fdb7..420273a 100644
--- a/third_party/WebKit/Source/core/html/HTMLSelectElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLSelectElement.h
@@ -191,7 +191,7 @@
 
     void defaultEventHandler(Event*) override;
 
-    void dispatchInputAndChangeEventForMenuList(bool requiresUserGesture = true);
+    void dispatchInputAndChangeEventForMenuList();
 
     void recalcListItems(bool updateSelectedStates = true) const;
 
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp
index fd027931..cf18359 100644
--- a/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp
@@ -517,13 +517,13 @@
             continue;
         Element* childElement = toElement(child);
         if (childElement->isDateTimeFieldElement()) {
-            // We need to pass the Font of this element because child elements
-            // can't resolve inherited style at this timing.
-            width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(style->font());
+            // We need to pass the ComputedStyle of this element because child
+            // elements can't resolve inherited style at this timing.
+            width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(*style);
         } else {
             // ::-webkit-datetime-edit-text case. It has no
             // border/padding/margin in html.css.
-            width += style->font().width(childElement->textContent());
+            width += DateTimeFieldElement::computeTextWidth(*style, childElement->textContent());
         }
     }
     style->setWidth(Length(ceilf(width), Fixed));
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.cpp
index 5f5dca0..d28e676d 100644
--- a/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.cpp
@@ -31,6 +31,8 @@
 #include "core/dom/Document.h"
 #include "core/dom/Text.h"
 #include "core/events/KeyboardEvent.h"
+#include "core/layout/TextRunConstructor.h"
+#include "core/style/ComputedStyle.h"
 #include "platform/text/PlatformLocale.h"
 #include "wtf/text/WTFString.h"
 
@@ -59,6 +61,11 @@
     HTMLSpanElement::trace(visitor);
 }
 
+float DateTimeFieldElement::computeTextWidth(const ComputedStyle& style, const String& text)
+{
+    return style.font().width(constructTextRun(style.font(), text, style));
+}
+
 void DateTimeFieldElement::defaultEventHandler(Event* event)
 {
     if (event->isKeyboardEvent()) {
@@ -192,7 +199,7 @@
     return m_fieldOwner ? m_fieldOwner->localeIdentifier() : nullAtom;
 }
 
-float DateTimeFieldElement::maximumWidth(const Font&)
+float DateTimeFieldElement::maximumWidth(const ComputedStyle&)
 {
     const float paddingLeftAndRight = 2; // This should match to html.css.
     return paddingLeftAndRight;
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.h b/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.h
index ad2821a..a0239df 100644
--- a/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.h
+++ b/third_party/WebKit/Source/core/html/shadow/DateTimeFieldElement.h
@@ -34,7 +34,6 @@
 
 class DateComponents;
 class DateTimeFieldsState;
-class Font;
 
 // DateTimeFieldElement is base class of date time field element.
 class DateTimeFieldElement : public HTMLSpanElement {
@@ -65,7 +64,7 @@
     void defaultEventHandler(Event*) override;
     virtual bool hasValue() const = 0;
     bool isDisabled() const;
-    virtual float maximumWidth(const Font&);
+    virtual float maximumWidth(const ComputedStyle&);
     virtual void populateDateTimeFieldsState(DateTimeFieldsState&) = 0;
     void removeEventHandler() { m_fieldOwner = nullptr; }
     void setDisabled();
@@ -79,6 +78,8 @@
     virtual String visibleValue() const = 0;
     DECLARE_VIRTUAL_TRACE();
 
+    static float computeTextWidth(const ComputedStyle&, const String&);
+
 protected:
     DateTimeFieldElement(Document&, FieldOwner&);
     void focusOnNextField();
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.cpp
index c3b79678..71365a2c 100644
--- a/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.cpp
@@ -74,12 +74,12 @@
     }
 }
 
-float DateTimeNumericFieldElement::maximumWidth(const Font& font)
+float DateTimeNumericFieldElement::maximumWidth(const ComputedStyle& style)
 {
-    float maximumWidth = font.width(m_placeholder);
-    maximumWidth = std::max(maximumWidth, font.width(formatValue(maximum())));
-    maximumWidth = std::max(maximumWidth, font.width(value()));
-    return maximumWidth + DateTimeFieldElement::maximumWidth(font);
+    float maximumWidth = computeTextWidth(style, m_placeholder);
+    maximumWidth = std::max(maximumWidth, computeTextWidth(style, formatValue(maximum())));
+    maximumWidth = std::max(maximumWidth, computeTextWidth(style, value()));
+    return maximumWidth + DateTimeFieldElement::maximumWidth(style);
 }
 
 int DateTimeNumericFieldElement::defaultValueForStepDown() const
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.h b/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.h
index fa5c0324..88c0d09 100644
--- a/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.h
+++ b/third_party/WebKit/Source/core/html/shadow/DateTimeNumericFieldElement.h
@@ -84,7 +84,7 @@
 private:
     // DateTimeFieldElement functions.
     void handleKeyboardEvent(KeyboardEvent*) final;
-    float maximumWidth(const Font&) override;
+    float maximumWidth(const ComputedStyle&) override;
     void stepDown() final;
     void stepUp() final;
     String value() const final;
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp
index 984a774b7..58cb90047 100644
--- a/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp
@@ -63,12 +63,12 @@
     ASSERT(m_minimumIndex <= m_maximumIndex);
 }
 
-float DateTimeSymbolicFieldElement::maximumWidth(const Font& font)
+float DateTimeSymbolicFieldElement::maximumWidth(const ComputedStyle& style)
 {
-    float maximumWidth = font.width(visibleEmptyValue());
+    float maximumWidth = computeTextWidth(style, visibleEmptyValue());
     for (unsigned index = 0; index < m_symbols.size(); ++index)
-        maximumWidth = std::max(maximumWidth, font.width(m_symbols[index]));
-    return maximumWidth + DateTimeFieldElement::maximumWidth(font);
+        maximumWidth = std::max(maximumWidth, computeTextWidth(style, m_symbols[index]));
+    return maximumWidth + DateTimeFieldElement::maximumWidth(style);
 }
 
 void DateTimeSymbolicFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEvent)
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.h b/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.h
index 9c1ec88..51124264 100644
--- a/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.h
+++ b/third_party/WebKit/Source/core/html/shadow/DateTimeSymbolicFieldElement.h
@@ -54,7 +54,7 @@
 
     // DateTimeFieldElement functions.
     void handleKeyboardEvent(KeyboardEvent*) final;
-    float maximumWidth(const Font&) override;
+    float maximumWidth(const ComputedStyle&) override;
     void stepDown() final;
     void stepUp() final;
     String value() const final;
diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
index 1794f1c2..dcf30ed 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
@@ -280,20 +280,15 @@
 
 void LayoutBoxModelObject::createLayer(PaintLayerType type)
 {
-
-    // Acquiring a PaintLayer may change the paint invalidation container. Therefore we must eagerly
-    // invalidate paint for this object before creating the layer.
-    if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && isRooted()) {
-        DisablePaintInvalidationStateAsserts invalidationDisabler;
-        DisableCompositingQueryAsserts compositingDisabler;
-
-        // It would be more correct to call invalidatePaintIncludingNonCompositingDescendants, but
-        // we do this instead to avoid performance issues when creating large numbers of layers.
-        const LayoutBoxModelObject& paintInvalidationContainer = containerForPaintInvalidationOnRootedTree();
-        invalidatePaintUsingContainer(
-            paintInvalidationContainer,
-            boundsRectForPaintInvalidation(&paintInvalidationContainer),
-            PaintInvalidationLayer);
+    // If the current paint invalidation container is not a stacking context and this object is
+    // a or treated as a stacking context, creating this layer may cause this object and its
+    // descendants to change paint invalidation container. Therefore we must eagerly invalidate
+    // them on the original paint invalidation container before creating the layer.
+    if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && isRooted() && styleRef().isTreatedAsOrStackingContext()) {
+        if (const LayoutBoxModelObject* currentPaintInvalidationContainer = containerForPaintInvalidation()) {
+            if (!currentPaintInvalidationContainer->styleRef().isStackingContext())
+                invalidatePaintIncludingNonSelfPaintingLayerDescendants(*currentPaintInvalidationContainer);
+        }
     }
 
     ASSERT(!m_layer);
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index 2499b03..f4b615a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -3388,6 +3388,18 @@
     traverseNonCompositingDescendants(const_cast<LayoutObject&>(object), Functor(paintInvalidationContainer));
 }
 
+void LayoutObject::invalidatePaintOfPreviousPaintInvalidationRect(const LayoutBoxModelObject& paintInvalidationContainer, PaintInvalidationReason reason) const
+{
+    // These disablers are valid because we want to use the current compositing/invalidation status.
+    DisablePaintInvalidationStateAsserts invalidationDisabler;
+    DisableCompositingQueryAsserts compositingDisabler;
+
+    LayoutRect invalidationRect = previousPaintInvalidationRect();
+    adjustInvalidationRectForCompositedScrolling(invalidationRect, paintInvalidationContainer);
+    invalidatePaintUsingContainer(paintInvalidationContainer, invalidationRect, PaintInvalidationLayer);
+    invalidateDisplayItemClients(paintInvalidationContainer, PaintInvalidationLayer, invalidationRect, invalidationRect);
+}
+
 void LayoutObject::invalidatePaintIncludingNonCompositingDescendants()
 {
     class Functor : public LayoutObjectTraversalFunctor {
@@ -3395,10 +3407,7 @@
         explicit Functor(const LayoutBoxModelObject& paintInvalidationContainer) : m_paintInvalidationContainer(paintInvalidationContainer) { }
         void operator()(LayoutObject& object) const override
         {
-            LayoutRect invalidationRect = object.previousPaintInvalidationRect();
-            object.adjustInvalidationRectForCompositedScrolling(invalidationRect, m_paintInvalidationContainer);
-            object.invalidatePaintUsingContainer(m_paintInvalidationContainer, invalidationRect, PaintInvalidationLayer);
-            object.invalidateDisplayItemClients(m_paintInvalidationContainer, PaintInvalidationLayer, invalidationRect, invalidationRect);
+            object.invalidatePaintOfPreviousPaintInvalidationRect(m_paintInvalidationContainer, PaintInvalidationLayer);
         }
     private:
         const LayoutBoxModelObject& m_paintInvalidationContainer;
@@ -3425,6 +3434,15 @@
     traverseNonCompositingDescendants(*this, Functor());
 }
 
+void LayoutObject::invalidatePaintIncludingNonSelfPaintingLayerDescendants(const LayoutBoxModelObject& paintInvalidationContainer)
+{
+    invalidatePaintOfPreviousPaintInvalidationRect(paintInvalidationContainer, PaintInvalidationLayer);
+    for (LayoutObject* child = slowFirstChild(); child; child = child->nextSibling()) {
+        if (!child->hasLayer() || !toLayoutBoxModelObject(child)->layer()->isSelfPaintingLayer())
+            child->invalidatePaintIncludingNonSelfPaintingLayerDescendants(paintInvalidationContainer);
+    }
+}
+
 void LayoutObject::setIsSlowRepaintObject(bool isSlowRepaintObject)
 {
     ASSERT(frameView());
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index 339ee246..11c46fa6 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1029,6 +1029,7 @@
     void invalidatePaintForOverflowIfNeeded();
 
     void invalidatePaintIncludingNonCompositingDescendants();
+    void invalidatePaintIncludingNonSelfPaintingLayerDescendants(const LayoutBoxModelObject& paintInvalidationContainer);
     void setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
 
     // Returns the rect that should have paint invalidated whenever this object changes. The rect is in the view's
@@ -1464,7 +1465,7 @@
 
     inline void invalidateContainerPreferredLogicalWidths();
 
-    void invalidatePaintIncludingNonCompositingDescendantsInternal(const LayoutBoxModelObject& repaintContainer);
+    void invalidatePaintOfPreviousPaintInvalidationRect(const LayoutBoxModelObject& paintInvalidationContainer, PaintInvalidationReason) const;
 
     LayoutRect previousSelectionRectForPaintInvalidation() const;
     void setPreviousSelectionRectForPaintInvalidation(const LayoutRect&);
diff --git a/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp b/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp
index 55f3d07..0dae1bd 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp
@@ -186,14 +186,7 @@
         oldChild.view()->setShouldDoFullPaintInvalidation();
         return;
     }
-
-    DisableCompositingQueryAsserts disabler;
-    // FIXME: We should not allow paint invalidation out of paint invalidation state. crbug.com/457415
-    DisablePaintInvalidationStateAsserts paintInvalidationAssertDisabler;
-    const LayoutBoxModelObject& paintInvalidationContainer = *oldChild.containerForPaintInvalidation();
-    const LayoutRect& invalidationRect = oldChild.previousPaintInvalidationRectIncludingCompositedScrolling(paintInvalidationContainer);
-    oldChild.invalidatePaintUsingContainer(paintInvalidationContainer, invalidationRect, PaintInvalidationLayoutObjectRemoval);
-    oldChild.invalidateDisplayItemClients(paintInvalidationContainer, PaintInvalidationLayoutObjectRemoval, invalidationRect, invalidationRect);
+    oldChild.invalidatePaintOfPreviousPaintInvalidationRect(*oldChild.containerForPaintInvalidation(), PaintInvalidationLayoutObjectRemoval);
 }
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp b/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp
index 06003e4..53a2b872 100644
--- a/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp
@@ -130,12 +130,6 @@
     // Early exit if the element touches the edges.
     LayoutUnit top = paintRect.y();
     LayoutUnit bottom = paintRect.maxY();
-    if (isSelected() && inlineBoxWrapper()) {
-        LayoutUnit selTop = paintOffset.y() + inlineBoxWrapper()->root().selectionTop();
-        LayoutUnit selBottom = paintOffset.y() + selTop + inlineBoxWrapper()->root().selectionHeight();
-        top = std::min(selTop, top);
-        bottom = std::max(selBottom, bottom);
-    }
 
     if (paintRect.x() >= paintInfo.rect.maxX() || paintRect.maxX() <= paintInfo.rect.x())
         return false;
@@ -438,10 +432,10 @@
 {
     ASSERT(!needsLayout());
 
-    if (!isSelected())
-        return LayoutRect();
-
     LayoutRect rect = localSelectionRect();
+    if (rect.isEmpty())
+        return rect;
+
     mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, 0);
     // FIXME: groupedMapping() leaks the squashing abstraction.
     if (paintInvalidationContainer->layer()->groupedMapping())
@@ -449,9 +443,9 @@
     return rect;
 }
 
-LayoutRect LayoutReplaced::localSelectionRect(bool checkWhetherSelected) const
+LayoutRect LayoutReplaced::localSelectionRect() const
 {
-    if (checkWhetherSelected && !isSelected())
+    if (selectionState() == SelectionNone)
         return LayoutRect();
 
     if (!inlineBoxWrapper()) {
@@ -480,41 +474,7 @@
         setPreviousPaintInvalidationRect(boundsRectForPaintInvalidation(containerForPaintInvalidation()));
 
     if (canUpdateSelectionOnRootLineBoxes())
-        inlineBoxWrapper()->root().setHasSelectedChildren(isSelected());
-}
-
-bool LayoutReplaced::isSelected() const
-{
-    SelectionState s = selectionState();
-    if (s == SelectionNone)
-        return false;
-    if (s == SelectionInside)
-        return true;
-
-    int selectionStart, selectionEnd;
-    selectionStartEnd(selectionStart, selectionEnd);
-    if (s == SelectionStart)
-        return selectionStart == 0;
-
-    int end = node()->hasChildren() ? node()->countChildren() : 1;
-    if (s == SelectionEnd)
-        return selectionEnd == end;
-    if (s == SelectionBoth)
-        return selectionStart == 0 && selectionEnd == end;
-
-    ASSERT(0);
-    return false;
-}
-LayoutRect LayoutReplaced::clippedOverflowRectForPaintInvalidation(const LayoutBoxModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) const
-{
-    if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
-        return LayoutRect();
-
-    // The selectionRect can project outside of the overflowRect, so take their union
-    // for paint invalidation to avoid selection painting glitches.
-    LayoutRect r = isSelected() ? localSelectionRect() : visualOverflowRect();
-    mapRectToPaintInvalidationBacking(paintInvalidationContainer, r, paintInvalidationState);
-    return r;
+        inlineBoxWrapper()->root().setHasSelectedChildren(state != SelectionNone);
 }
 
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutReplaced.h b/third_party/WebKit/Source/core/layout/LayoutReplaced.h
index 84002e1..1d61071 100644
--- a/third_party/WebKit/Source/core/layout/LayoutReplaced.h
+++ b/third_party/WebKit/Source/core/layout/LayoutReplaced.h
@@ -67,12 +67,10 @@
     bool canHaveChildren() const override { return false; }
     bool shouldPaint(const PaintInfo&, const LayoutPoint&) const;
     virtual void paintReplaced(const PaintInfo&, const LayoutPoint&) const { }
-    LayoutRect localSelectionRect(bool checkWhetherSelected = true) const; // This is in local coordinates, but it's a physical rect (so the top left corner is physical top left).
+    LayoutRect localSelectionRect() const; // This is in local coordinates, but it's a physical rect (so the top left corner is physical top left).
 
     void paint(const PaintInfo&, const LayoutPoint&) const override;
 
-    bool isSelected() const;
-
 protected:
     void willBeDestroyed() override;
 
@@ -105,8 +103,6 @@
 private:
     void computePreferredLogicalWidths() final;
 
-    LayoutRect clippedOverflowRectForPaintInvalidation(const LayoutBoxModelObject* paintInvalidationContainer, const PaintInvalidationState* = nullptr) const override;
-
     PositionWithAffinity positionForPoint(const LayoutPoint&) final;
 
     bool canBeSelectionLeaf() const override { return true; }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
index 506e2f1..eabb068 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableRow.cpp
@@ -144,13 +144,13 @@
 
     LayoutTableCell* cell = toLayoutTableCell(child);
 
+    ASSERT(!beforeChild || beforeChild->isTableCell());
+    LayoutBox::addChild(cell, beforeChild);
+
     // Generated content can result in us having a null section so make sure to null check our parent.
     if (parent())
         section()->addCell(cell, this);
 
-    ASSERT(!beforeChild || beforeChild->isTableCell());
-    LayoutBox::addChild(cell, beforeChild);
-
     if (beforeChild || nextRow())
         section()->setNeedsCellRecalc();
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
index 420b909..332dc177 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
@@ -200,6 +200,32 @@
         m_grid[row].row.grow(effectiveColumnCount);
 }
 
+static inline void checkThatVectorIsDOMOrdered(const Vector<LayoutTableCell*, 1>& cells)
+{
+#ifndef NDEBUG
+    // This function should be called on a non-empty vector.
+    ASSERT(cells.size() > 0);
+
+    const LayoutTableCell* previousCell = cells[0];
+    for (size_t i = 1; i < cells.size(); ++i) {
+        const LayoutTableCell* currentCell = cells[i];
+        // The check assumes that all cells belong to the same row group.
+        ASSERT(previousCell->section() == currentCell->section());
+
+        // 2 overlapping cells can't be on the same row.
+        ASSERT(currentCell->row() != previousCell->row());
+
+        // Look backwards in the tree for the previousCell's row. If we are
+        // DOM ordered, we should find it.
+        const LayoutTableRow* row = currentCell->row();
+        for (; row && row != previousCell->row(); row = row->previousRow()) { }
+        ASSERT(row == previousCell->row());
+
+        previousCell = currentCell;
+    }
+#endif // NDEBUG
+}
+
 void LayoutTableSection::addCell(LayoutTableCell* cell, LayoutTableRow* row)
 {
     // We don't insert the cell if we need cell recalc as our internal columns' representation
@@ -246,6 +272,7 @@
             CellStruct& c = cellAt(insertionRow + r, m_cCol);
             ASSERT(cell);
             c.cells.append(cell);
+            checkThatVectorIsDOMOrdered(c.cells);
             // If cells overlap then we take the slow path for painting.
             if (c.cells.size() > 1)
                 m_hasMultipleCellLevels = true;
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.h b/third_party/WebKit/Source/core/layout/LayoutTableSection.h
index a4cdcfd..f3bbd6a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableSection.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.h
@@ -128,9 +128,15 @@
 
     typedef Vector<LayoutTableCell*, 2> SpanningLayoutTableCells;
 
+    // CellStruct represents the cells that occupy an (N, M) position in the
+    // table grid.
     struct CellStruct {
         ALLOW_ONLY_INLINE_ALLOCATION();
     public:
+        // All the cells that fills this grid "slot".
+        // Due to colspan / rowpsan, it is possible to have overlapping cells
+        // (see class comment about an example).
+        // This Vector is sorted in DOM order.
         Vector<LayoutTableCell*, 1> cells;
         bool inColSpan; // true for columns after the first in a colspan
 
@@ -139,6 +145,17 @@
         {
         }
 
+        // This is the cell in the grid "slot" that is on top of the others
+        // (aka the last cell in DOM order for this slot).
+        //
+        // This is the cell originating from this slot if it exists.
+        //
+        // The concept of a primary cell is dubious at most as it doesn't
+        // correspond to a DOM or rendering concept. Also callers should be
+        // careful about assumptions about it. For example, even though the
+        // primary cell is visibly the top most, it is not guaranteed to be
+        // the only one visible for this slot due to different visual
+        // overflow rectangles.
         LayoutTableCell* primaryCell()
         {
             return hasCells() ? cells[cells.size() - 1] : 0;
@@ -380,6 +397,11 @@
     HashSet<LayoutTableCell*> m_overflowingCells;
     bool m_forceSlowPaintPathWithOverflowingCell;
 
+    // This boolean tracks if we have cells overlapping due to rowspan / colspan
+    // (see class comment above about when it could appear).
+    //
+    // The use is to disable a painting optimization where we just paint the
+    // invalidated cells.
     bool m_hasMultipleCellLevels;
 
     // This map holds the collapsed border values for cells with collapsed borders.
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index bdb983a..46f51bb1 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -1581,16 +1581,20 @@
             }
 
             layerChanged = true;
-            if (scrollingCoordinator)
+            if (scrollingCoordinator) {
                 scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_owningLayer.scrollableArea());
+                scrollingCoordinator->scrollableAreasDidChange();
+            }
         }
     } else if (m_scrollingLayer) {
         m_scrollingLayer = nullptr;
         m_scrollingContentsLayer = nullptr;
         m_scrollingBlockSelectionLayer = nullptr;
         layerChanged = true;
-        if (scrollingCoordinator)
+        if (scrollingCoordinator) {
             scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_owningLayer.scrollableArea());
+            scrollingCoordinator->scrollableAreasDidChange();
+        }
     }
 
     return layerChanged;
diff --git a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
index 0fce6b65..59c68cbe 100644
--- a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
+++ b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
@@ -334,7 +334,7 @@
     return checkSide;
 }
 
-inline LayoutUnit inlineLogicalWidth(LineLayoutItem child, bool start = true, bool end = true)
+inline LayoutUnit inlineLogicalWidthFromAncestorsIfNeeded(LineLayoutItem child, bool start = true, bool end = true)
 {
     unsigned lineDepth = 1;
     LayoutUnit extraWidth = 0;
@@ -378,7 +378,7 @@
     } else {
         positionedObjects.append(box);
     }
-    m_width.addUncommittedWidth(inlineLogicalWidth(box).toFloat());
+    m_width.addUncommittedWidth(inlineLogicalWidthFromAncestorsIfNeeded(box).toFloat());
     // Reset prior line break context characters.
     m_layoutTextInfo.m_lineBreakIterator.resetPriorContext();
 }
@@ -455,7 +455,7 @@
         }
     }
 
-    m_width.addUncommittedWidth((inlineLogicalWidth(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)).toFloat());
+    m_width.addUncommittedWidth((inlineLogicalWidthFromAncestorsIfNeeded(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)).toFloat());
 }
 
 inline void BreakingContext::handleReplaced()
@@ -483,7 +483,7 @@
 
     // Optimize for a common case. If we can't find whitespace after the list
     // item, then this is all moot.
-    LayoutUnit replacedLogicalWidth = m_block.logicalWidthForChild(replacedBox) + m_block.marginStartForChild(replacedBox) + m_block.marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.object());
+    LayoutUnit replacedLogicalWidth = m_block.logicalWidthForChild(replacedBox) + m_block.marginStartForChild(replacedBox) + m_block.marginEndForChild(replacedBox) + inlineLogicalWidthFromAncestorsIfNeeded(m_current.object());
     if (m_current.object().isListMarker()) {
         if (m_blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) {
             // Like with inline flows, we start ignoring spaces to make sure that any
@@ -559,7 +559,7 @@
     float lastSpaceWordSpacing = 0;
     float wordSpacingForWordMeasurement = 0;
 
-    float wrapW = m_width.uncommittedWidth();
+    float widthFromLastBreakingOpportunity = m_width.uncommittedWidth();
     float charWidth = 0;
     // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
     // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
@@ -615,10 +615,10 @@
         bool applyWordSpacing = false;
 
         if (breakWords && !midWordBreak) {
-            wrapW += charWidth;
+            widthFromLastBreakingOpportunity += charWidth;
             bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.offset() + 1 < layoutText.textLength() && U16_IS_TRAIL(layoutText.uncheckedCharacterAt(m_current.offset() + 1));
-            charWidth = textWidth(layoutText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, m_collapseWhiteSpace);
-            midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth();
+            charWidth = textWidth(layoutText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + widthFromLastBreakingOpportunity, m_collapseWhiteSpace);
+            midWordBreak = m_width.committedWidth() + widthFromLastBreakingOpportunity + charWidth > m_width.availableWidth();
         }
 
         int nextBreakablePosition = m_current.nextBreakablePosition();
@@ -651,22 +651,22 @@
             wordMeasurement.endOffset = m_current.offset();
             wordMeasurement.startOffset = lastSpace;
 
-            float additionalTempWidth;
+            float lastWidthMeasurement;
             if (wordTrailingSpaceWidth && c == spaceCharacter)
-                additionalTempWidth = textWidth(layoutText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds) - wordTrailingSpaceWidth;
+                lastWidthMeasurement = textWidth(layoutText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds) - wordTrailingSpaceWidth;
             else
-                additionalTempWidth = textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds);
+                lastWidthMeasurement = textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds);
 
-            wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
+            wordMeasurement.width = lastWidthMeasurement + wordSpacingForWordMeasurement;
             wordMeasurement.glyphBounds.move(wordSpacingForWordMeasurement, 0);
-            additionalTempWidth += lastSpaceWordSpacing;
-            m_width.addUncommittedWidth(additionalTempWidth);
+            lastWidthMeasurement += lastSpaceWordSpacing;
+            m_width.addUncommittedWidth(lastWidthMeasurement);
 
-            if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && additionalTempWidth)
-                m_width.setTrailingWhitespaceWidth(additionalTempWidth);
+            if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && lastWidthMeasurement)
+                m_width.setTrailingWhitespaceWidth(lastWidthMeasurement);
 
             if (!m_appliedStartWidth) {
-                m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object(), true, false).toFloat());
+                m_width.addUncommittedWidth(inlineLogicalWidthFromAncestorsIfNeeded(m_current.object(), true, false).toFloat());
                 m_appliedStartWidth = true;
             }
 
@@ -714,8 +714,8 @@
                         return false;
                     }
                 } else {
-                    if (!betweenWords || (midWordBreak && !m_autoWrap))
-                        m_width.addUncommittedWidth(-additionalTempWidth);
+                    if (!betweenWords || (midWordBreak && !m_autoWrap) || (breakAll && !m_currentCharacterIsSpace))
+                        m_width.addUncommittedWidth(-lastWidthMeasurement);
                     if (hyphenWidth) {
                         // Subtract the width of the soft hyphen out since we fit on a line.
                         m_width.addUncommittedWidth(-hyphenWidth);
@@ -735,7 +735,7 @@
 
             if (m_autoWrap && betweenWords) {
                 m_width.commit();
-                wrapW = 0;
+                widthFromLastBreakingOpportunity = 0;
                 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
                 // Auto-wrapping text should not wrap in the middle of a word once it has had an
                 // opportunity to break after a word.
@@ -752,7 +752,7 @@
             if (betweenWords) {
                 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
                 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
-                lastSpace = m_current.offset();
+                lastSpace = !breakAll || m_currentCharacterIsSpace ? m_current.offset() : lastSpace;
             }
 
             if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) {
@@ -816,21 +816,21 @@
     wordMeasurement.layoutText = layoutText;
 
     // IMPORTANT: current.offset() is > layoutText.textLength() here!
-    float additionalTempWidth = 0;
+    float lastWidthMeasurement = 0;
     wordMeasurement.startOffset = lastSpace;
     wordMeasurement.endOffset = m_current.offset();
     if (!m_ignoringSpaces) {
-        additionalTempWidth = textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds);
-        wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
+        lastWidthMeasurement = textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, &wordMeasurement.glyphBounds);
+        wordMeasurement.width = lastWidthMeasurement + wordSpacingForWordMeasurement;
         wordMeasurement.glyphBounds.move(wordSpacingForWordMeasurement, 0);
     }
-    additionalTempWidth += lastSpaceWordSpacing;
+    lastWidthMeasurement += lastSpaceWordSpacing;
 
-    LayoutUnit inlineLogicalTempWidth = inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, m_includeEndWidth);
-    m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth);
+    LayoutUnit additionalWidthFromAncestors = inlineLogicalWidthFromAncestorsIfNeeded(m_current.object(), !m_appliedStartWidth, m_includeEndWidth);
+    m_width.addUncommittedWidth(lastWidthMeasurement + additionalWidthFromAncestors);
 
-    if (m_collapseWhiteSpace && m_currentCharacterIsSpace && additionalTempWidth)
-        m_width.setTrailingWhitespaceWidth(additionalTempWidth + inlineLogicalTempWidth);
+    if (m_collapseWhiteSpace && m_currentCharacterIsSpace && lastWidthMeasurement)
+        m_width.setTrailingWhitespaceWidth(lastWidthMeasurement + additionalWidthFromAncestors);
 
     m_includeEndWidth = false;
 
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
index a36d8aa..2750c93 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
@@ -333,7 +333,7 @@
     if (m_hasBoxDecorationBackground || hasOverflowModel()) {
         // The selectionRect can project outside of the overflowRect, so take their union
         // for paint invalidation to avoid selection painting glitches.
-        LayoutRect decoratedPaintInvalidationRect = unionRect(localSelectionRect(false), visualOverflowRect());
+        LayoutRect decoratedPaintInvalidationRect = unionRect(localSelectionRect(), visualOverflowRect());
         paintInvalidationRect.unite(decoratedPaintInvalidationRect);
     }
 
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
index e292c4d..ab9e7dca 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
@@ -74,7 +74,7 @@
     void updateHaveWheelEventHandlers();
     void updateHaveScrollEventHandlers();
 
-    // Should be called whenever scrollable area set changes.
+    // Should be called whenever a scrollable area is added or removed, or gains/loses a composited layer.
     void scrollableAreasDidChange();
 
     // Should be called whenever the slow repaint objects counter changes between zero and one.
diff --git a/third_party/WebKit/Source/core/paint/ListMarkerPainter.cpp b/third_party/WebKit/Source/core/paint/ListMarkerPainter.cpp
index ced95df..3d7e8a4 100644
--- a/third_party/WebKit/Source/core/paint/ListMarkerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/ListMarkerPainter.cpp
@@ -12,6 +12,7 @@
 #include "core/paint/BlockPainter.h"
 #include "core/paint/LayoutObjectDrawingRecorder.h"
 #include "core/paint/PaintInfo.h"
+#include "platform/RuntimeEnabledFeatures.h"
 #include "platform/geometry/LayoutPoint.h"
 #include "platform/graphics/GraphicsContextStateSaver.h"
 #include "wtf/text/CharacterNames.h"
@@ -53,7 +54,8 @@
 
     LayoutPoint boxOrigin(paintOffset + m_layoutListMarker.location());
     LayoutRect overflowRect(m_layoutListMarker.visualOverflowRect());
-    if (m_layoutListMarker.selectionState() != SelectionNone)
+    if (!RuntimeEnabledFeatures::selectionPaintingWithoutSelectionGapsEnabled()
+        && m_layoutListMarker.selectionState() != SelectionNone)
         overflowRect.unite(m_layoutListMarker.localSelectionRect());
     overflowRect.moveBy(boxOrigin);
 
@@ -80,7 +82,8 @@
         return;
     }
 
-    if (m_layoutListMarker.selectionState() != SelectionNone) {
+    if (!RuntimeEnabledFeatures::selectionPaintingWithoutSelectionGapsEnabled()
+        && m_layoutListMarker.selectionState() != SelectionNone) {
         LayoutRect selRect = m_layoutListMarker.localSelectionRect();
         selRect.moveBy(boxOrigin);
         context->fillRect(pixelSnappedIntRect(selRect), m_layoutListMarker.listItem()->selectionBackgroundColor());
diff --git a/third_party/WebKit/Source/core/paint/PartPainter.cpp b/third_party/WebKit/Source/core/paint/PartPainter.cpp
index 8b4327d..fbe7456b 100644
--- a/third_party/WebKit/Source/core/paint/PartPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PartPainter.cpp
@@ -17,6 +17,29 @@
 
 namespace blink {
 
+bool PartPainter::isSelected() const
+{
+    SelectionState s = m_layoutPart.selectionState();
+    if (s == SelectionNone)
+        return false;
+    if (s == SelectionInside)
+        return true;
+
+    int selectionStart, selectionEnd;
+    m_layoutPart.selectionStartEnd(selectionStart, selectionEnd);
+    if (s == SelectionStart)
+        return selectionStart == 0;
+
+    int end = m_layoutPart.node()->hasChildren() ? m_layoutPart.node()->countChildren() : 1;
+    if (s == SelectionEnd)
+        return selectionEnd == end;
+    if (s == SelectionBoth)
+        return selectionStart == 0 && selectionEnd == end;
+
+    ASSERT(0);
+    return false;
+}
+
 void PartPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 {
     if (!m_layoutPart.shouldPaint(paintInfo, paintOffset))
@@ -60,7 +83,7 @@
     }
 
     // Paint a partially transparent wash over selected widgets.
-    if (m_layoutPart.isSelected() && !paintInfo.isPrinting() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutPart, paintInfo.phase, adjustedPaintOffset)) {
+    if (isSelected() && !paintInfo.isPrinting() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutPart, paintInfo.phase, adjustedPaintOffset)) {
         LayoutRect rect = m_layoutPart.localSelectionRect();
         rect.moveBy(adjustedPaintOffset);
         IntRect selectionRect = pixelSnappedIntRect(rect);
diff --git a/third_party/WebKit/Source/core/paint/PartPainter.h b/third_party/WebKit/Source/core/paint/PartPainter.h
index a8595b5..eac057c 100644
--- a/third_party/WebKit/Source/core/paint/PartPainter.h
+++ b/third_party/WebKit/Source/core/paint/PartPainter.h
@@ -22,6 +22,8 @@
     void paintContents(const PaintInfo&, const LayoutPoint&);
 
 private:
+    bool isSelected() const;
+
     const LayoutPart& m_layoutPart;
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/elementsTreeOutline.css b/third_party/WebKit/Source/devtools/front_end/elements/elementsTreeOutline.css
index 7ea8ca1..a4967ea 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/elementsTreeOutline.css
+++ b/third_party/WebKit/Source/devtools/front_end/elements/elementsTreeOutline.css
@@ -337,7 +337,7 @@
 }
 
 @media (-webkit-min-device-pixel-ratio: 1.5) {
-.elements-disclosure li.selected .gutter-container {
+.elements-disclosure li.selected .gutter-container:not(.has-decorations) {
     -webkit-mask-image: url(Images/toolbarButtonGlyphs_2x.png);
 }
 } /* media */
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index d7d4c16..357bdb1 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -390,10 +390,8 @@
 
     _registerForwardedShortcuts: function()
     {
-        /** @const */ var forwardedActions = ["main.reload", "main.hard-reload", "main.toggle-dock", "debugger.toggle-breakpoints-active", "debugger.toggle-pause"];
+        /** @const */ var forwardedActions = ["main.toggle-dock", "debugger.toggle-breakpoints-active", "debugger.toggle-pause"];
         var actionKeys = WebInspector.shortcutRegistry.keysForActions(forwardedActions).map(WebInspector.KeyboardShortcut.keyCodeAndModifiersFromKey);
-
-        actionKeys.push({keyCode: WebInspector.KeyboardShortcut.Keys.F8.code});
         InspectorFrontendHost.setWhitelistedShortcuts(JSON.stringify(actionKeys));
     },
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js b/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
index f56780e..eae8e71 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js
@@ -146,7 +146,7 @@
         menuItemElement._subItems = item.subItems;
 
         // Occupy the same space on the left in all items.
-        var checkMarkElement = menuItemElement.createChild("span", "soft-context-menu-item-checkmark");
+        var checkMarkElement = menuItemElement.createChild("span", "soft-context-menu-item-checkmark checkmark");
         checkMarkElement.textContent = "\u2713 "; // Checkmark Unicode symbol
         checkMarkElement.style.opacity = "0";
 
@@ -357,11 +357,10 @@
 
             if (event)
                 event.consume(true);
-        } else if (this._parentMenu && this._contextMenuElement.parentElement) {
+        } else if (this._parentMenu && this._contextMenuElement.parentElementOrShadowHost()) {
             this._discardSubMenus();
             if (closeParentMenus)
                 this._parentMenu._discardMenu(closeParentMenus, event);
-
             if (event)
                 event.consume(true);
         }
@@ -375,7 +374,7 @@
     {
         if (this._subMenu)
             this._subMenu._discardSubMenus();
-        this._contextMenuElement.remove();
+        this.element.remove();
         if (this._parentMenu)
             delete this._parentMenu._subMenu;
     }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/softContextMenu.css b/third_party/WebKit/Source/devtools/front_end/ui/softContextMenu.css
index 76f8e23..755c1c3 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/softContextMenu.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/softContextMenu.css
@@ -59,9 +59,10 @@
 
 .soft-context-menu-item-submenu-arrow {
     color: #222;
-    float: right;
     pointer-events: none;
     font-size: 11px;
+    flex: 1 1 auto;
+    text-align: right;
 }
 
 .soft-context-menu-item-mouse-over .soft-context-menu-item-checkmark {
diff --git a/third_party/WebKit/Source/platform/heap/Handle.h b/third_party/WebKit/Source/platform/heap/Handle.h
index fd9415a..d087caa 100644
--- a/third_party/WebKit/Source/platform/heap/Handle.h
+++ b/third_party/WebKit/Source/platform/heap/Handle.h
@@ -869,15 +869,83 @@
     template<typename Derived> friend class VisitorHelper;
 };
 
-// Comparison operators between (Weak)Members and Persistents
+// UntracedMember is a pointer to an on-heap object that is not traced for some
+// reason. Please don't use this unless you understand what you're doing.
+// Basically, all pointers to on-heap objects must be stored in either of
+// Persistent, Member or WeakMember. It is not allowed to leave raw pointers to
+// on-heap objects. However, there can be scenarios where you have to use raw
+// pointers for some reason, and in that case you can use UntracedMember. Of
+// course, it must be guaranteed that the pointing on-heap object is kept alive
+// while the raw pointer is pointing to the object.
+template<typename T>
+class UntracedMember final : public Member<T> {
+public:
+    UntracedMember() : Member<T>() { }
+
+    UntracedMember(std::nullptr_t) : Member<T>(nullptr) { }
+
+    UntracedMember(T* raw) : Member<T>(raw) { }
+
+    template<typename U>
+    UntracedMember(const RawPtr<U>& other) : Member<T>(other) { }
+
+    template<typename U>
+    UntracedMember(const Persistent<U>& other) : Member<T>(other) { }
+
+    template<typename U>
+    UntracedMember(const Member<U>& other) : Member<T>(other) { }
+
+    UntracedMember(WTF::HashTableDeletedValueType x) : Member<T>(x) { }
+
+    template<typename U>
+    UntracedMember& operator=(const Persistent<U>& other)
+    {
+        this->m_raw = other;
+        this->checkPointer();
+        return *this;
+    }
+
+    template<typename U>
+    UntracedMember& operator=(const Member<U>& other)
+    {
+        this->m_raw = other;
+        this->checkPointer();
+        return *this;
+    }
+
+    template<typename U>
+    UntracedMember& operator=(U* other)
+    {
+        this->m_raw = other;
+        this->checkPointer();
+        return *this;
+    }
+
+    template<typename U>
+    UntracedMember& operator=(const RawPtr<U>& other)
+    {
+        this->m_raw = other;
+        this->checkPointer();
+        return *this;
+    }
+
+    UntracedMember& operator=(std::nullptr_t)
+    {
+        this->m_raw = nullptr;
+        return *this;
+    }
+};
+
+// Comparison operators between (Weak)Members, Persistents, and UntracedMembers.
 template<typename T, typename U> inline bool operator==(const Member<T>& a, const Member<U>& b) { return a.get() == b.get(); }
 template<typename T, typename U> inline bool operator!=(const Member<T>& a, const Member<U>& b) { return a.get() != b.get(); }
+template<typename T, typename U> inline bool operator==(const Persistent<T>& a, const Persistent<U>& b) { return a.get() == b.get(); }
+template<typename T, typename U> inline bool operator!=(const Persistent<T>& a, const Persistent<U>& b) { return a.get() != b.get(); }
+
 template<typename T, typename U> inline bool operator==(const Member<T>& a, const Persistent<U>& b) { return a.get() == b.get(); }
 template<typename T, typename U> inline bool operator!=(const Member<T>& a, const Persistent<U>& b) { return a.get() != b.get(); }
 template<typename T, typename U> inline bool operator==(const Persistent<T>& a, const Member<U>& b) { return a.get() == b.get(); }
 template<typename T, typename U> inline bool operator!=(const Persistent<T>& a, const Member<U>& b) { return a.get() != b.get(); }
-template<typename T, typename U> inline bool operator==(const Persistent<T>& a, const Persistent<U>& b) { return a.get() == b.get(); }
-template<typename T, typename U> inline bool operator!=(const Persistent<T>& a, const Persistent<U>& b) { return a.get() != b.get(); }
 
 template<typename T>
 class DummyBase {
@@ -925,6 +993,7 @@
 #define RawPtrWillBePersistent blink::Persistent
 #define RawPtrWillBeWeakMember blink::WeakMember
 #define RawPtrWillBeWeakPersistent blink::WeakPersistent
+#define RawPtrWillBeUntracedMember blink::UntracedMember
 #define OwnPtrWillBeCrossThreadPersistent blink::CrossThreadPersistent
 #define OwnPtrWillBeMember blink::Member
 #define OwnPtrWillBePersistent blink::Persistent
@@ -1008,6 +1077,7 @@
 #define RawPtrWillBePersistent WTF::RawPtr
 #define RawPtrWillBeWeakMember WTF::RawPtr
 #define RawPtrWillBeWeakPersistent WTF::RawPtr
+#define RawPtrWillBeUntracedMember WTF::RawPtr
 #define OwnPtrWillBeCrossThreadPersistent WTF::OwnPtr
 #define OwnPtrWillBeMember WTF::OwnPtr
 #define OwnPtrWillBePersistent WTF::OwnPtr
@@ -1198,6 +1268,13 @@
     static const bool canMoveWithMemcpy = true;
 };
 
+template <typename T> struct VectorTraits<blink::UntracedMember<T>> : VectorTraitsBase<blink::UntracedMember<T>> {
+    static const bool needsDestruction = false;
+    static const bool canInitializeWithMemset = true;
+    static const bool canClearUnusedSlotsWithMemset = true;
+    static const bool canMoveWithMemcpy = true;
+};
+
 template <typename T> struct VectorTraits<blink::HeapVector<T, 0>> : VectorTraitsBase<blink::HeapVector<T, 0>> {
     static const bool needsDestruction = false;
     static const bool canInitializeWithMemset = true;
@@ -1289,6 +1366,32 @@
     }
 };
 
+template<typename T> struct HashTraits<blink::UntracedMember<T>> : SimpleClassHashTraits<blink::UntracedMember<T>> {
+    static const bool needsDestruction = false;
+    // FIXME: The distinction between PeekInType and PassInType is there for
+    // the sake of the reference counting handles. When they are gone the two
+    // types can be merged into PassInType.
+    // FIXME: Implement proper const'ness for iterator types.
+    using PeekInType = RawPtr<T>;
+    using PassInType = RawPtr<T>;
+    using IteratorGetType = blink::UntracedMember<T>*;
+    using IteratorConstGetType = const blink::UntracedMember<T>*;
+    using IteratorReferenceType = blink::UntracedMember<T>&;
+    using IteratorConstReferenceType = const blink::UntracedMember<T>&;
+    static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { return *x; }
+    static IteratorConstReferenceType getToReferenceConstConversion(IteratorConstGetType x) { return *x; }
+    // FIXME: Similarly, there is no need for a distinction between PeekOutType
+    // and PassOutType without reference counting.
+    using PeekOutType = T*;
+    using PassOutType = T*;
+
+    template<typename U>
+    static void store(const U& value, blink::UntracedMember<T>& storage) { storage = value; }
+
+    static PeekOutType peek(const blink::UntracedMember<T>& value) { return value; }
+    static PassOutType passOut(const blink::UntracedMember<T>& value) { return value; }
+};
+
 template<typename T> struct PtrHash<blink::Member<T>> : PtrHash<T*> {
     template<typename U>
     static unsigned hash(const U& key) { return PtrHash<T*>::hash(key); }
@@ -1301,6 +1404,9 @@
 template<typename T> struct PtrHash<blink::WeakMember<T>> : PtrHash<blink::Member<T>> {
 };
 
+template<typename T> struct PtrHash<blink::UntracedMember<T>> : PtrHash<blink::Member<T>> {
+};
+
 // PtrHash is the default hash for hash tables with members.
 template<typename T> struct DefaultHash<blink::Member<T>> {
     using Hash = PtrHash<blink::Member<T>>;
@@ -1310,6 +1416,10 @@
     using Hash = PtrHash<blink::WeakMember<T>>;
 };
 
+template<typename T> struct DefaultHash<blink::UntracedMember<T>> {
+    using Hash = PtrHash<blink::UntracedMember<T>>;
+};
+
 template<typename T>
 struct NeedsTracing<blink::Member<T>> {
     static const bool value = true;
diff --git a/third_party/WebKit/Source/platform/heap/Heap.h b/third_party/WebKit/Source/platform/heap/Heap.h
index 5d45a3d..355b1719 100644
--- a/third_party/WebKit/Source/platform/heap/Heap.h
+++ b/third_party/WebKit/Source/platform/heap/Heap.h
@@ -43,6 +43,10 @@
 
 namespace blink {
 
+template<typename T> class Member;
+template<typename T> class WeakMember;
+template<typename T> class UntracedMember;
+
 template<typename T, bool = NeedsAdjustAndMark<T>::value> class ObjectAliveTrait;
 
 template<typename T>
@@ -100,6 +104,11 @@
         return isHeapObjectAlive(member.get());
     }
     template<typename T>
+    static inline bool isHeapObjectAlive(const UntracedMember<T>& member)
+    {
+        return isHeapObjectAlive(member.get());
+    }
+    template<typename T>
     static inline bool isHeapObjectAlive(const RawPtr<T>& ptr)
     {
         return isHeapObjectAlive(ptr.get());
diff --git a/third_party/WebKit/Source/platform/heap/HeapAllocator.cpp b/third_party/WebKit/Source/platform/heap/HeapAllocator.cpp
index 7531234..8d27428 100644
--- a/third_party/WebKit/Source/platform/heap/HeapAllocator.cpp
+++ b/third_party/WebKit/Source/platform/heap/HeapAllocator.cpp
@@ -88,6 +88,12 @@
 
 bool HeapAllocator::backingShrink(void* address, size_t quantizedCurrentSize, size_t quantizedShrunkSize)
 {
+    // We shrink the object only if the shrinking will make a non-small
+    // prompt-free block.
+    // FIXME: Optimize the threshold size.
+    if (quantizedCurrentSize <= quantizedShrunkSize + sizeof(HeapObjectHeader) + sizeof(void*) * 32)
+        return true;
+
     if (!address)
         return true;
 
@@ -106,12 +112,6 @@
     HeapObjectHeader* header = HeapObjectHeader::fromPayload(address);
     ASSERT(header->checkHeader());
     NormalPageHeap* heap = static_cast<NormalPage*>(page)->heapForNormalPage();
-    // We shrink the object only if the shrinking will make a non-small
-    // prompt-free block.
-    // FIXME: Optimize the threshold size.
-    if (quantizedCurrentSize <= quantizedShrunkSize + sizeof(HeapObjectHeader) + sizeof(void*) * 32 && !heap->isObjectAllocatedAtAllocationPoint(header))
-        return true;
-
     bool succeededAtAllocationPoint = heap->shrinkObject(header, quantizedShrunkSize);
     if (succeededAtAllocationPoint)
         state->allocationPointAdjusted(heap->heapIndex());
diff --git a/third_party/WebKit/Source/platform/heap/HeapPage.cpp b/third_party/WebKit/Source/platform/heap/HeapPage.cpp
index 78c725e..a94679f 100644
--- a/third_party/WebKit/Source/platform/heap/HeapPage.cpp
+++ b/third_party/WebKit/Source/platform/heap/HeapPage.cpp
@@ -699,7 +699,7 @@
     size_t allocationSize = Heap::allocationSizeFromSize(newSize);
     ASSERT(allocationSize > header->size());
     size_t expandSize = allocationSize - header->size();
-    if (isObjectAllocatedAtAllocationPoint(header) && expandSize <= m_remainingAllocationSize) {
+    if (header->payloadEnd() == m_currentAllocationPoint && expandSize <= m_remainingAllocationSize) {
         m_currentAllocationPoint += expandSize;
         m_remainingAllocationSize -= expandSize;
 
@@ -719,7 +719,7 @@
     size_t allocationSize = Heap::allocationSizeFromSize(newSize);
     ASSERT(header->size() > allocationSize);
     size_t shrinkSize = header->size() - allocationSize;
-    if (isObjectAllocatedAtAllocationPoint(header)) {
+    if (header->payloadEnd() == m_currentAllocationPoint) {
         m_currentAllocationPoint -= shrinkSize;
         m_remainingAllocationSize += shrinkSize;
         SET_MEMORY_INACCESSIBLE(m_currentAllocationPoint, shrinkSize);
diff --git a/third_party/WebKit/Source/platform/heap/HeapPage.h b/third_party/WebKit/Source/platform/heap/HeapPage.h
index cdbb50f..29ddbd30 100644
--- a/third_party/WebKit/Source/platform/heap/HeapPage.h
+++ b/third_party/WebKit/Source/platform/heap/HeapPage.h
@@ -781,11 +781,6 @@
     bool shrinkObject(HeapObjectHeader*, size_t);
     void decreasePromptlyFreedSize(size_t size) { m_promptlyFreedSize -= size; }
 
-    bool isObjectAllocatedAtAllocationPoint(HeapObjectHeader* header)
-    {
-        return header->payloadEnd() == m_currentAllocationPoint;
-    }
-
 private:
     void allocatePage();
     Address lazySweepPages(size_t, size_t gcInfoIndex) override;
diff --git a/third_party/WebKit/Source/web/WebDOMError.cpp b/third_party/WebKit/Source/web/WebDOMError.cpp
deleted file mode 100644
index 3118e9c..0000000
--- a/third_party/WebKit/Source/web/WebDOMError.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "public/web/WebDOMError.h"
-
-#include "bindings/core/v8/V8Binding.h"
-#include "bindings/core/v8/V8DOMError.h"
-#include "core/dom/DOMError.h"
-#include "wtf/PassOwnPtr.h"
-
-namespace blink {
-
-WebDOMError WebDOMError::create(const WebString& name, const WebString& message)
-{
-    return DOMError::create(name, message);
-}
-
-void WebDOMError::reset()
-{
-    m_private.reset();
-}
-
-void WebDOMError::assign(const WebDOMError& other)
-{
-    m_private = other.m_private;
-}
-
-WebString WebDOMError::name() const
-{
-    if (!m_private.get())
-        return WebString();
-    return m_private->name();
-}
-
-WebString WebDOMError::message() const
-{
-    if (!m_private.get())
-        return WebString();
-    return m_private->message();
-}
-
-v8::Local<v8::Value>  WebDOMError::toV8Value(v8::Local<v8::Object> creationContext, v8::Isolate* isolate)
-{
-    // We no longer use |creationContext| because it's often misused and points
-    // to a context faked by user script.
-    ASSERT(creationContext->CreationContext() == isolate->GetCurrentContext());
-    if (!m_private.get())
-        return v8::Local<v8::Value>();
-    return toV8(m_private.get(), isolate->GetCurrentContext()->Global(), isolate);
-}
-
-WebDOMError::WebDOMError(DOMError* error)
-    : m_private(error)
-{
-}
-
-WebDOMError& WebDOMError::operator=(DOMError* error)
-{
-    m_private = error;
-    return *this;
-}
-
-} // namespace blink
diff --git a/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp b/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp
index 05125f1..1f2c807 100644
--- a/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp
+++ b/third_party/WebKit/Source/web/WebDevToolsAgentImpl.cpp
@@ -160,7 +160,7 @@
         agent->flushPendingProtocolNotifications();
 
         Vector<WebViewImpl*> views;
-        Vector<WebFrameWidgetImpl*> widgets;
+        WillBeHeapVector<RawPtrWillBeMember<WebFrameWidgetImpl>> widgets;
 
         // 1. Disable input events.
         const HashSet<WebViewImpl*>& viewImpls = WebViewImpl::allInstances();
@@ -172,9 +172,9 @@
             view->setIgnoreInputEvents(true);
         }
 
-        const HashSet<WebFrameWidgetImpl*>& widgetImpls = WebFrameWidgetImpl::allInstances();
-        HashSet<WebFrameWidgetImpl*>::const_iterator widgetImplsEnd = widgetImpls.end();
-        for (HashSet<WebFrameWidgetImpl*>::const_iterator it =  widgetImpls.begin(); it != widgetImplsEnd; ++it) {
+        const WebFrameWidgetsSet& widgetImpls = WebFrameWidgetImpl::allInstances();
+        WebFrameWidgetsSet::const_iterator widgetImplsEnd = widgetImpls.end();
+        for (WebFrameWidgetsSet::const_iterator it =  widgetImpls.begin(); it != widgetImplsEnd; ++it) {
             WebFrameWidgetImpl* widget = *it;
             m_frozenWidgets.add(widget);
             widgets.append(widget);
@@ -200,7 +200,7 @@
                 (*it)->setIgnoreInputEvents(false);
             }
         }
-        for (Vector<WebFrameWidgetImpl*>::iterator it = widgets.begin(); it != widgets.end(); ++it) {
+        for (WillBeHeapVector<RawPtrWillBeMember<WebFrameWidgetImpl>>::iterator it = widgets.begin(); it != widgets.end(); ++it) {
             if (m_frozenWidgets.contains(*it)) {
                 // The widget was not closed during the dispatch.
                 (*it)->setIgnoreInputEvents(false);
@@ -226,8 +226,7 @@
     OwnPtr<WebDevToolsAgentClient::WebKitClientMessageLoop> m_messageLoop;
     typedef HashSet<WebViewImpl*> FrozenViewsSet;
     FrozenViewsSet m_frozenViews;
-    typedef HashSet<WebFrameWidgetImpl*> FrozenWidgetsSet;
-    FrozenWidgetsSet m_frozenWidgets;
+    WebFrameWidgetsSet m_frozenWidgets;
     static ClientMessageLoopAdapter* s_instance;
 };
 
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index c2b98fd..984f2ef 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -47,7 +47,6 @@
 #include "core/page/Page.h"
 #include "platform/KeyboardCodes.h"
 #include "platform/NotImplemented.h"
-#include "public/web/WebBeginFrameArgs.h"
 #include "public/web/WebWidgetClient.h"
 #include "web/ContextMenuAllowedScope.h"
 #include "web/WebDevToolsAgentImpl.h"
@@ -83,9 +82,9 @@
 }
 
 // static
-HashSet<WebFrameWidgetImpl*>& WebFrameWidgetImpl::allInstances()
+WebFrameWidgetsSet& WebFrameWidgetImpl::allInstances()
 {
-    DEFINE_STATIC_LOCAL(HashSet<WebFrameWidgetImpl*>, allInstances, ());
+    DEFINE_STATIC_LOCAL(WebFrameWidgetsSet, allInstances, ());
     return allInstances;
 }
 
@@ -246,11 +245,11 @@
     // FIXME: Implement full screen for out-of-process iframes.
 }
 
-void WebFrameWidgetImpl::beginFrame(const WebBeginFrameArgs& frameTime)
+void WebFrameWidgetImpl::beginFrame(double lastFrameTimeMonotonic)
 {
-    TRACE_EVENT1("blink", "WebFrameWidgetImpl::beginFrame", "frameTime", frameTime.lastFrameTimeMonotonic);
-    ASSERT(frameTime.lastFrameTimeMonotonic);
-    PageWidgetDelegate::animate(*page(), frameTime.lastFrameTimeMonotonic);
+    TRACE_EVENT1("blink", "WebFrameWidgetImpl::beginFrame", "frameTime", lastFrameTimeMonotonic);
+    ASSERT(lastFrameTimeMonotonic);
+    PageWidgetDelegate::animate(*page(), lastFrameTimeMonotonic);
 }
 
 void WebFrameWidgetImpl::layout()
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
index dfebc2a..f91e79d 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
@@ -57,13 +57,16 @@
 class WebLayerTreeView;
 class WebMouseEvent;
 class WebMouseWheelEvent;
+class WebFrameWidgetImpl;
+
+using WebFrameWidgetsSet = WillBePersistentHeapHashSet<RawPtrWillBeWeakMember<WebFrameWidgetImpl>>;
 
 class WebFrameWidgetImpl final : public RefCountedWillBeGarbageCollectedFinalized<WebFrameWidgetImpl>
     , public WebFrameWidget
     , public PageWidgetEventHandler {
 public:
     static WebFrameWidgetImpl* create(WebWidgetClient*, WebLocalFrame*);
-    static HashSet<WebFrameWidgetImpl*>& allInstances();
+    static WebFrameWidgetsSet& allInstances();
 
     // WebWidget functions:
     void close() override;
@@ -75,7 +78,7 @@
     void willEndLiveResize() override;
     void didEnterFullScreen() override;
     void didExitFullScreen() override;
-    void beginFrame(const WebBeginFrameArgs&) override;
+    void beginFrame(double lastFrameTimeMonotonic) override;
     void layout() override;
     void paint(WebCanvas*, const WebRect&) override;
     void layoutAndPaintAsync(WebLayoutAndPaintAsyncCallback*) override;
diff --git a/third_party/WebKit/Source/web/WebPagePopupImpl.cpp b/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
index ed32068..6f14730 100644
--- a/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
+++ b/third_party/WebKit/Source/web/WebPagePopupImpl.cpp
@@ -357,11 +357,11 @@
     return m_popupClient->contentSize();
 }
 
-void WebPagePopupImpl::beginFrame(const WebBeginFrameArgs& frameTime)
+void WebPagePopupImpl::beginFrame(double lastFrameTimeMonotonic)
 {
     if (!m_page)
         return;
-    // FIXME: This should use frameTime.lastFrameTimeMonotonic but doing so
+    // FIXME: This should use lastFrameTimeMonotonic but doing so
     // breaks tests.
     PageWidgetDelegate::animate(*m_page, monotonicallyIncreasingTime());
 }
diff --git a/third_party/WebKit/Source/web/WebPagePopupImpl.h b/third_party/WebKit/Source/web/WebPagePopupImpl.h
index d1e9163..4e434ab 100644
--- a/third_party/WebKit/Source/web/WebPagePopupImpl.h
+++ b/third_party/WebKit/Source/web/WebPagePopupImpl.h
@@ -74,7 +74,7 @@
 private:
     // WebWidget functions
     WebSize size() override;
-    void beginFrame(const WebBeginFrameArgs&) override;
+    void beginFrame(double lastFrameTimeMonotonic) override;
     void layout() override;
     void willCloseLayerTreeView() override;
     void paint(WebCanvas*, const WebRect&) override;
diff --git a/third_party/WebKit/Source/web/WebViewFrameWidget.cpp b/third_party/WebKit/Source/web/WebViewFrameWidget.cpp
index 30de52e..e160d23 100644
--- a/third_party/WebKit/Source/web/WebViewFrameWidget.cpp
+++ b/third_party/WebKit/Source/web/WebViewFrameWidget.cpp
@@ -72,9 +72,9 @@
     return m_webView->didExitFullScreen();
 }
 
-void WebViewFrameWidget::beginFrame(const WebBeginFrameArgs& frameTime)
+void WebViewFrameWidget::beginFrame(double lastFrameTimeMonotonic)
 {
-    return m_webView->beginFrame(frameTime);
+    return m_webView->beginFrame(lastFrameTimeMonotonic);
 }
 
 void WebViewFrameWidget::layout()
diff --git a/third_party/WebKit/Source/web/WebViewFrameWidget.h b/third_party/WebKit/Source/web/WebViewFrameWidget.h
index 32b7f18..4c1db73 100644
--- a/third_party/WebKit/Source/web/WebViewFrameWidget.h
+++ b/third_party/WebKit/Source/web/WebViewFrameWidget.h
@@ -46,7 +46,7 @@
     void willEndLiveResize() override;
     void didEnterFullScreen() override;
     void didExitFullScreen() override;
-    void beginFrame(const WebBeginFrameArgs& frameTime) override;
+    void beginFrame(double lastFrameTimeMonotonic) override;
     void layout() override;
     void paint(WebCanvas*, const WebRect& viewPort) override;
     void paintCompositedDeprecated(WebCanvas*, const WebRect&) override;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index f508e81..0caf920 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -131,7 +131,6 @@
 #include "public/web/WebAXObject.h"
 #include "public/web/WebActiveWheelFlingParameters.h"
 #include "public/web/WebAutofillClient.h"
-#include "public/web/WebBeginFrameArgs.h"
 #include "public/web/WebElement.h"
 #include "public/web/WebFrame.h"
 #include "public/web/WebFrameClient.h"
@@ -1864,14 +1863,14 @@
     m_fullscreenController->didExitFullScreen();
 }
 
-void WebViewImpl::beginFrame(const WebBeginFrameArgs& frameTime)
+void WebViewImpl::beginFrame(double lastFrameTimeMonotonic)
 {
-    TRACE_EVENT1("blink", "WebViewImpl::beginFrame", "frameTime", frameTime.lastFrameTimeMonotonic);
-    ASSERT(frameTime.lastFrameTimeMonotonic);
+    TRACE_EVENT1("blink", "WebViewImpl::beginFrame", "frameTime", lastFrameTimeMonotonic);
+    ASSERT(lastFrameTimeMonotonic);
 
     // Create synthetic wheel events as necessary for fling.
     if (m_gestureAnimation) {
-        if (m_gestureAnimation->animate(frameTime.lastFrameTimeMonotonic))
+        if (m_gestureAnimation->animate(lastFrameTimeMonotonic))
             scheduleAnimation();
         else {
             endActiveFlingAnimation();
@@ -1888,7 +1887,7 @@
     if (!m_page)
         return;
 
-    PageWidgetDelegate::animate(*m_page, frameTime.lastFrameTimeMonotonic);
+    PageWidgetDelegate::animate(*m_page, lastFrameTimeMonotonic);
 }
 
 void WebViewImpl::layout()
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index 000d805a..febe2c0 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -109,7 +109,7 @@
     void didEnterFullScreen() override;
     void didExitFullScreen() override;
 
-    void beginFrame(const WebBeginFrameArgs&) override;
+    void beginFrame(double lastFrameTimeMonotonic) override;
 
     void layout() override;
     void paint(WebCanvas*, const WebRect&) override;
diff --git a/third_party/WebKit/Source/web/tests/sim/SimCompositor.cpp b/third_party/WebKit/Source/web/tests/sim/SimCompositor.cpp
index 3bdc736..59e393e2 100644
--- a/third_party/WebKit/Source/web/tests/sim/SimCompositor.cpp
+++ b/third_party/WebKit/Source/web/tests/sim/SimCompositor.cpp
@@ -75,8 +75,7 @@
     // Always advance the time as if the compositor was running at 60fps.
     m_lastFrameTimeMonotonic = monotonicallyIncreasingTime() + 0.016;
 
-    WebBeginFrameArgs args(m_lastFrameTimeMonotonic, 0, 0);
-    m_webViewImpl->beginFrame(args);
+    m_webViewImpl->beginFrame(m_lastFrameTimeMonotonic);
     m_webViewImpl->layout();
 
     LocalFrame* root = m_webViewImpl->mainFrameImpl()->frame();
diff --git a/third_party/WebKit/Source/web/web.gypi b/third_party/WebKit/Source/web/web.gypi
index ff62502..100c957 100644
--- a/third_party/WebKit/Source/web/web.gypi
+++ b/third_party/WebKit/Source/web/web.gypi
@@ -118,7 +118,6 @@
       'WebCSSParser.cpp',
       'WebDOMActivityLogger.cpp',
       'WebDOMCustomEvent.cpp',
-      'WebDOMError.cpp',
       'WebDOMEvent.cpp',
       'WebDOMFileSystem.cpp',
       'WebDOMMediaStreamTrack.cpp',
diff --git a/third_party/WebKit/public/blink_headers.gypi b/third_party/WebKit/public/blink_headers.gypi
index 87ba6ef4..55b7ffb 100644
--- a/third_party/WebKit/public/blink_headers.gypi
+++ b/third_party/WebKit/public/blink_headers.gypi
@@ -337,7 +337,6 @@
       "web/WebArrayBufferConverter.h",
       "web/WebArrayBufferView.h",
       "web/WebAutofillClient.h",
-      "web/WebBeginFrameArgs.h",
       "web/WebBindings.h",
       "web/WebBlob.h",
       "web/WebCSSParser.h",
@@ -356,7 +355,6 @@
       "web/WebCustomElement.h",
       "web/WebDOMActivityLogger.h",
       "web/WebDOMCustomEvent.h",
-      "web/WebDOMError.h",
       "web/WebDOMEvent.h",
       "web/WebDOMFileSystem.h",
       "web/WebDOMMediaStreamTrack.h",
diff --git a/third_party/WebKit/public/web/WebBeginFrameArgs.h b/third_party/WebKit/public/web/WebBeginFrameArgs.h
deleted file mode 100644
index 5a31028..0000000
--- a/third_party/WebKit/public/web/WebBeginFrameArgs.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WebBeginFrameArgs_h
-#define WebBeginFrameArgs_h
-
-namespace blink {
-
-struct WebBeginFrameArgs {
-    WebBeginFrameArgs(double lastFrameTimeMonotonic, double deadline, double interval)
-        : lastFrameTimeMonotonic(lastFrameTimeMonotonic)
-        , deadline(deadline)
-        , interval(interval)
-    { }
-
-    // FIXME: Upgrade the time in CLOCK_MONOTONIC values to use a TimeTick like
-    // class rather than a bare double.
-
-    // FIXME: Extend this class to include the fields from Chrome
-    // BeginFrameArgs structure.
-
-    // Time in CLOCK_MONOTONIC that is the most recent vsync time.
-    double lastFrameTimeMonotonic;
-
-    // Time in CLOCK_MONOTONIC by which the renderer should finish producing the current frame. 0 means a deadline wasn't set.
-    double deadline;
-
-    // Expected delta between two successive frame times. 0 if a regular interval isn't available.
-    double interval;
-};
-
-} // namespace blink
-
-#endif
diff --git a/third_party/WebKit/public/web/WebDOMError.h b/third_party/WebKit/public/web/WebDOMError.h
deleted file mode 100644
index 44c4c47..0000000
--- a/third_party/WebKit/public/web/WebDOMError.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef WebDOMError_h
-#define WebDOMError_h
-
-#include "public/platform/WebCommon.h"
-#include "public/platform/WebPrivatePtr.h"
-#include "public/platform/WebString.h"
-
-namespace v8 {
-class Isolate;
-class Object;
-class Value;
-template <class T> class Local;
-}
-
-namespace blink {
-
-class DOMError;
-
-class WebDOMError {
-public:
-    ~WebDOMError() { reset(); }
-
-    WebDOMError() { }
-    WebDOMError(const WebDOMError& b) { assign(b); }
-    WebDOMError& operator=(const WebDOMError& b)
-    {
-        assign(b);
-        return *this;
-    }
-
-    BLINK_EXPORT static WebDOMError create(const WebString& name, const WebString& message);
-
-    BLINK_EXPORT void reset();
-    BLINK_EXPORT void assign(const WebDOMError&);
-
-    BLINK_EXPORT WebString name() const;
-    BLINK_EXPORT WebString message() const;
-
-    BLINK_EXPORT v8::Local<v8::Value> toV8Value(v8::Local<v8::Object> creationContext, v8::Isolate*);
-
-#if BLINK_IMPLEMENTATION
-    WebDOMError(DOMError*);
-    WebDOMError& operator=(DOMError*);
-#endif
-
-protected:
-    WebPrivatePtr<DOMError> m_private;
-};
-
-} // namespace blink
-
-#endif // WebDOMError_h
diff --git a/third_party/WebKit/public/web/WebWidget.h b/third_party/WebKit/public/web/WebWidget.h
index e9f186a8..aacecf1 100644
--- a/third_party/WebKit/public/web/WebWidget.h
+++ b/third_party/WebKit/public/web/WebWidget.h
@@ -40,7 +40,6 @@
 #include "../platform/WebRect.h"
 #include "../platform/WebSize.h"
 #include "../platform/WebTopControlsState.h"
-#include "WebBeginFrameArgs.h"
 #include "WebCompositionUnderline.h"
 #include "WebTextDirection.h"
 #include "WebTextInputInfo.h"
@@ -93,7 +92,7 @@
 
     // Called to update imperative animation state. This should be called before
     // paint, although the client can rate-limit these calls.
-    virtual void beginFrame(const WebBeginFrameArgs& frameTime) { }
+    virtual void beginFrame(double lastFrameTimeMonotonic) { }
 
     // Called to layout the WebWidget. This MUST be called before Paint,
     // and it may result in calls to WebWidgetClient::didInvalidateRect.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 75b4374..cf7be26 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -5820,6 +5820,20 @@
   </summary>
 </histogram>
 
+<histogram name="DataReductionProxy.EnabledState"
+    enum="DataReductionProxyEnabledState">
+  <owner>bengr@chromium.org</owner>
+  <owner>rajendrant@chromium.org</owner>
+  <summary>
+    Samples of user interactions with the Data Saver settings menu across all
+    platforms (android, IOS, desktop extension). Data is collected whenever user
+    toggles the ON/OFF switch in the settings menu for reducing data usage.
+
+    For desktop, Off-to-On/On-to-Off histogram count will increment when the
+    data saver extension is installed/uninstalled respectively as well.
+  </summary>
+</histogram>
+
 <histogram name="DataReductionProxy.HeaderTamperDetectionHTTP">
   <owner>xingx@chromium.org</owner>
   <owner>bolian@chromium.org</owner>
@@ -55090,6 +55104,11 @@
   <int value="10" label="Bypass due to any network error"/>
 </enum>
 
+<enum name="DataReductionProxyEnabledState" type="int">
+  <int value="0" label="Off-to-On"/>
+  <int value="1" label="On-to-Off"/>
+</enum>
+
 <enum name="DataReductionProxyLoFiImplicitOptOutAction" type="int">
   <int value="0" label="Lo-Fi disabled for the remainder of the session"/>
   <int value="1" label="Lo-Fi disabled until next opt out epoch"/>
diff --git a/tools/telemetry/telemetry/timeline/thread.py b/tools/telemetry/telemetry/timeline/thread.py
index 23ed4ce..f04f9da 100644
--- a/tools/telemetry/telemetry/timeline/thread.py
+++ b/tools/telemetry/telemetry/timeline/thread.py
@@ -136,11 +136,10 @@
       raise ValueError(
           'Slice %s end time is before its start.' % curr_slice.name)
     curr_slice.duration = end_timestamp - curr_slice.start
-    if end_thread_timestamp != None:
-      if curr_slice.thread_start == None:
-        raise ValueError(
-            'EndSlice with thread_timestamp called on open slice without ' +
-            'thread_timestamp')
+    # On Windows, it is possible to have a value for |end_thread_timestamp|
+    # but not for |curr_slice.thread_start|, because it takes some time to
+    # initialize the thread time timer.
+    if curr_slice.thread_start != None and end_thread_timestamp != None:
       curr_slice.thread_duration = (end_thread_timestamp -
                                     curr_slice.thread_start)
     curr_slice.did_not_finish = False
diff --git a/tools/valgrind/gtest_exclude/ipc_tests.gtest_mac.txt b/tools/valgrind/gtest_exclude/ipc_tests.gtest_mac.txt
new file mode 100644
index 0000000..592197a
--- /dev/null
+++ b/tools/valgrind/gtest_exclude/ipc_tests.gtest_mac.txt
@@ -0,0 +1,2 @@
+# Flaky under Valgrind. See http://crbug.com/543429
+IPCAttachmentBrokerMacTest.SendSharedMemoryHandleToSelf
diff --git a/tools/valgrind/gtest_exclude/remoting_unittests.gtest_mac.txt b/tools/valgrind/gtest_exclude/remoting_unittests.gtest_mac.txt
new file mode 100644
index 0000000..585b98401
--- /dev/null
+++ b/tools/valgrind/gtest_exclude/remoting_unittests.gtest_mac.txt
@@ -0,0 +1,2 @@
+# Failing on Mac. https://crbug.com/543431
+JingleSessionTest.TestQuicStreamChannel
diff --git a/ui/base/ime/dummy_text_input_client.cc b/ui/base/ime/dummy_text_input_client.cc
index c660bd92..dcc8fb4 100644
--- a/ui/base/ime/dummy_text_input_client.cc
+++ b/ui/base/ime/dummy_text_input_client.cc
@@ -29,8 +29,7 @@
 void DummyTextInputClient::InsertText(const base::string16& text) {
 }
 
-void DummyTextInputClient::InsertChar(base::char16 ch, int flags) {
-}
+void DummyTextInputClient::InsertChar(const KeyEvent& event) {}
 
 TextInputType DummyTextInputClient::GetTextInputType() const {
   return text_input_type_;
diff --git a/ui/base/ime/dummy_text_input_client.h b/ui/base/ime/dummy_text_input_client.h
index 2c26f89e..7f5f7f3 100644
--- a/ui/base/ime/dummy_text_input_client.h
+++ b/ui/base/ime/dummy_text_input_client.h
@@ -21,7 +21,7 @@
   void ConfirmCompositionText() override;
   void ClearCompositionText() override;
   void InsertText(const base::string16& text) override;
-  void InsertChar(base::char16 ch, int flags) override;
+  void InsertChar(const KeyEvent& event) override;
   TextInputType GetTextInputType() const override;
   TextInputMode GetTextInputMode() const override;
   int GetTextInputFlags() const override;
diff --git a/ui/base/ime/input_method_auralinux.cc b/ui/base/ime/input_method_auralinux.cc
index ade6577..ca9931f 100644
--- a/ui/base/ime/input_method_auralinux.cc
+++ b/ui/base/ime/input_method_auralinux.cc
@@ -97,8 +97,11 @@
   // Processes the result text before composition for sync mode.
   if (client && !result_text_.empty()) {
     if (filtered && NeedInsertChar()) {
-      for (const auto ch : result_text_)
-        client->InsertChar(ch, event->flags());
+      for (const auto ch : result_text_) {
+        ui::KeyEvent ch_event(*event);
+        ch_event.set_character(ch);
+        client->InsertChar(ch_event);
+      }
     } else {
       // If |filtered| is false, that means the IME wants to commit some text
       // but still release the key to the application. For example, Korean IME
@@ -146,7 +149,7 @@
       // DispatchKeyEventPostIME may cause the current text input client change.
       base::char16 ch = event->GetCharacter();
       if (ch && GetTextInputClient())
-        GetTextInputClient()->InsertChar(ch, event->flags());
+        GetTextInputClient()->InsertChar(*event);
       should_stop_propagation = true;
     }
   }
diff --git a/ui/base/ime/input_method_auralinux_unittest.cc b/ui/base/ime/input_method_auralinux_unittest.cc
index 7716e9ed..ba713416 100644
--- a/ui/base/ime/input_method_auralinux_unittest.cc
+++ b/ui/base/ime/input_method_auralinux_unittest.cc
@@ -222,9 +222,9 @@
     composition_text.clear();
   }
 
-  void InsertChar(base::char16 ch, int flags) override {
+  void InsertChar(const ui::KeyEvent& event) override {
     std::stringstream ss;
-    ss << ch;
+    ss << event.GetCharacter();
     TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16("keypress:") +
                                             base::ASCIIToUTF16(ss.str()));
   }
diff --git a/ui/base/ime/input_method_chromeos.cc b/ui/base/ime/input_method_chromeos.cc
index 1665269..a2ee046f 100644
--- a/ui/base/ime/input_method_chromeos.cc
+++ b/ui/base/ime/input_method_chromeos.cc
@@ -414,7 +414,7 @@
   // to send corresponding character to the focused text input client.
   uint16 ch = event->GetCharacter();
   if (ch)
-    client->InsertChar(ch, event->flags());
+    client->InsertChar(*event);
 }
 
 void InputMethodChromeOS::ProcessInputMethodResult(ui::KeyEvent* event,
@@ -426,7 +426,9 @@
     if (handled && NeedInsertChar()) {
       for (base::string16::const_iterator i = result_text_.begin();
            i != result_text_.end(); ++i) {
-        client->InsertChar(*i, event->flags());
+        ui::KeyEvent ch_event(*event);
+        ch_event.set_character(*i);
+        client->InsertChar(ch_event);
       }
     } else {
       client->InsertText(result_text_);
diff --git a/ui/base/ime/input_method_chromeos_unittest.cc b/ui/base/ime/input_method_chromeos_unittest.cc
index c1dcdf21..0ca3c32 100644
--- a/ui/base/ime/input_method_chromeos_unittest.cc
+++ b/ui/base/ime/input_method_chromeos_unittest.cc
@@ -256,9 +256,9 @@
   void InsertText(const base::string16& text) override {
     inserted_text_ = text;
   }
-  void InsertChar(base::char16 ch, int flags) override {
-    inserted_char_ = ch;
-    inserted_char_flags_ = flags;
+  void InsertChar(const KeyEvent& event) override {
+    inserted_char_ = event.GetCharacter();
+    inserted_char_flags_ = event.flags();
   }
   TextInputType GetTextInputType() const override { return input_type_; }
   TextInputMode GetTextInputMode() const override { return input_mode_; }
diff --git a/ui/base/ime/input_method_minimal.cc b/ui/base/ime/input_method_minimal.cc
index cd007021..9cb61612 100644
--- a/ui/base/ime/input_method_minimal.cc
+++ b/ui/base/ime/input_method_minimal.cc
@@ -37,7 +37,7 @@
   if (event->type() == ET_KEY_PRESSED && GetTextInputClient()) {
     const uint16 ch = event->GetCharacter();
     if (ch) {
-      GetTextInputClient()->InsertChar(ch, event->flags());
+      GetTextInputClient()->InsertChar(*event);
       event->StopPropagation();
     }
   }
diff --git a/ui/base/ime/input_method_win.cc b/ui/base/ime/input_method_win.cc
index 484be060..ef500a7 100644
--- a/ui/base/ime/input_method_win.cc
+++ b/ui/base/ime/input_method_win.cc
@@ -82,8 +82,8 @@
       break;
     case WM_CHAR:
     case WM_SYSCHAR:
-      original_result = OnChar(
-          event.hwnd, event.message, event.wParam, event.lParam, &handled);
+      original_result = OnChar(event.hwnd, event.message, event.wParam,
+                               event.lParam, event, &handled);
       break;
     case WM_IME_NOTIFY:
       original_result = OnImeNotify(
@@ -108,7 +108,8 @@
   BOOL handled = FALSE;
   if (native_key_event.message == WM_CHAR) {
     OnChar(native_key_event.hwnd, native_key_event.message,
-           native_key_event.wParam, native_key_event.lParam, &handled);
+           native_key_event.wParam, native_key_event.lParam, native_key_event,
+           &handled);
     if (handled)
       event->StopPropagation();
     return;
@@ -175,7 +176,7 @@
 
   for (size_t i = 0; i < char_msgs.size(); ++i) {
     MSG msg = char_msgs[i];
-    OnChar(msg.hwnd, msg.message, msg.wParam, msg.lParam, &handled);
+    OnChar(msg.hwnd, msg.message, msg.wParam, msg.lParam, msg, &handled);
   }
 }
 
@@ -255,6 +256,7 @@
                                UINT message,
                                WPARAM wparam,
                                LPARAM lparam,
+                               const base::NativeEvent& event,
                                BOOL* handled) {
   *handled = TRUE;
 
@@ -271,8 +273,10 @@
       accept_carriage_return_ = true;
     // Conditionally ignore '\r' events to work around crbug.com/319100.
     // TODO(yukawa, IME): Figure out long-term solution.
-    if (ch != kCarriageReturn || accept_carriage_return_)
-      GetTextInputClient()->InsertChar(ch, ui::GetModifiersFromKeyState());
+    if (ch != kCarriageReturn || accept_carriage_return_) {
+      ui::KeyEvent char_event(event);
+      GetTextInputClient()->InsertChar(char_event);
+    }
   }
 
   // Explicitly show the system menu at a good location on [Alt]+[Space].
@@ -589,9 +593,9 @@
 void InputMethodWin::DispatchFabricatedKeyEvent(ui::KeyEvent* event) {
   if (event->is_char()) {
     if (GetTextInputClient()) {
-      GetTextInputClient()->InsertChar(
-          static_cast<base::char16>(event->key_code()),
-          ui::GetModifiersFromKeyState());
+      ui::KeyEvent ch_event(*event);
+      ch_event.set_character(static_cast<base::char16>(event->key_code()));
+      GetTextInputClient()->InsertChar(ch_event);
       return;
     }
   }
diff --git a/ui/base/ime/input_method_win.h b/ui/base/ime/input_method_win.h
index 7cc347a..d813e4ea 100644
--- a/ui/base/ime/input_method_win.h
+++ b/ui/base/ime/input_method_win.h
@@ -50,6 +50,7 @@
                  UINT message,
                  WPARAM wparam,
                  LPARAM lparam,
+                 const base::NativeEvent& event,
                  BOOL* handled);
 
   LRESULT OnImeSetContext(HWND window_handle,
diff --git a/ui/base/ime/remote_input_method_win.cc b/ui/base/ime/remote_input_method_win.cc
index 1a589bb..e9f7fc2 100644
--- a/ui/base/ime/remote_input_method_win.cc
+++ b/ui/base/ime/remote_input_method_win.cc
@@ -183,9 +183,7 @@
     if (event->HasNativeEvent()) {
       const base::NativeEvent& native_key_event = event->native_event();
       if (native_key_event.message == WM_CHAR && text_input_client_) {
-        text_input_client_->InsertChar(
-            static_cast<base::char16>(native_key_event.wParam),
-            ui::GetModifiersFromKeyState());
+        text_input_client_->InsertChar(*event);
         event->StopPropagation();
       }
       return;
@@ -193,9 +191,7 @@
 
     if (event->is_char()) {
       if (text_input_client_) {
-        text_input_client_->InsertChar(
-            event->GetCharacter(),
-            ui::GetModifiersFromKeyState());
+        text_input_client_->InsertChar(*event);
       }
       event->StopPropagation();
       return;
@@ -319,8 +315,12 @@
       // According to the comment in text_input_client.h,
       // TextInputClient::InsertText should never be called when the
       // text input type is TEXT_INPUT_TYPE_NONE.
-      for (size_t i = 0; i < text.size(); ++i)
-        text_input_client_->InsertChar(text[i], 0);
+
+      for (size_t i = 0; i < text.size(); ++i) {
+        ui::KeyEvent char_event(text[i], static_cast<ui::KeyboardCode>(text[i]),
+                                ui::EF_NONE);
+        text_input_client_->InsertChar(char_event);
+      }
       return;
     }
     text_input_client_->InsertText(text);
diff --git a/ui/base/ime/remote_input_method_win_unittest.cc b/ui/base/ime/remote_input_method_win_unittest.cc
index 686937b..0970de0 100644
--- a/ui/base/ime/remote_input_method_win_unittest.cc
+++ b/ui/base/ime/remote_input_method_win_unittest.cc
@@ -79,8 +79,8 @@
   void SetCompositionText(const ui::CompositionText& composition) override {
     ++call_count_set_composition_text_;
   }
-  void InsertChar(base::char16 ch, int flags) override {
-    inserted_text_.append(1, ch);
+  void InsertChar(const ui::KeyEvent& event) override {
+    inserted_text_.append(1, event.GetCharacter());
     ++call_count_insert_char_;
   }
   void InsertText(const base::string16& text) override {
diff --git a/ui/base/ime/text_input_client.h b/ui/base/ime/text_input_client.h
index 390a96b..ae2ee6f7 100644
--- a/ui/base/ime/text_input_client.h
+++ b/ui/base/ime/text_input_client.h
@@ -21,6 +21,8 @@
 
 namespace ui {
 
+class KeyEvent;
+
 // An interface implemented by a View that needs text input support.
 class UI_BASE_IME_EXPORT TextInputClient {
  public:
@@ -46,14 +48,14 @@
   virtual void InsertText(const base::string16& text) = 0;
 
   // Inserts a single char at the insertion point. Unlike above InsertText()
-  // method, this method has an extra |flags| parameter indicating the modifier
-  // key states when the character is generated. This method should only be
+  // method, this method takes an |event| parameter indicating
+  // the event that was unprocessed. This method should only be
   // called when a key press is not handled by the input method but still
   // generates a character (eg. by the keyboard driver). In another word, the
   // preceding key press event should not be a VKEY_PROCESSKEY.
   // This method will be called whenever a char is generated by the keyboard,
   // even if the current text input type is TEXT_INPUT_TYPE_NONE.
-  virtual void InsertChar(base::char16 ch, int flags) = 0;
+  virtual void InsertChar(const ui::KeyEvent& event) = 0;
 
   // Input context information -------------------------------------------------
 
diff --git a/ui/events/win/events_win.cc b/ui/events/win/events_win.cc
index 52d1f47..1f50e5d8 100644
--- a/ui/events/win/events_win.cc
+++ b/ui/events/win/events_win.cc
@@ -91,6 +91,7 @@
   return native_event.message == WM_KEYDOWN ||
          native_event.message == WM_SYSKEYDOWN ||
          native_event.message == WM_CHAR ||
+         native_event.message == WM_SYSCHAR ||
          native_event.message == WM_KEYUP ||
          native_event.message == WM_SYSKEYUP;
 }
@@ -150,6 +151,7 @@
     case WM_KEYDOWN:
     case WM_SYSKEYDOWN:
     case WM_CHAR:
+    case WM_SYSCHAR:
       return ET_KEY_PRESSED;
     // The WM_DEADCHAR message is posted to the window with the keyboard focus
     // when a WM_KEYUP message is translated. This happens for special keyboard
@@ -265,7 +267,7 @@
 }
 
 bool IsCharFromNative(const base::NativeEvent& native_event) {
-  return native_event.message == WM_CHAR;
+  return native_event.message == WM_CHAR || native_event.message == WM_SYSCHAR;
 }
 
 int GetChangedMouseButtonFlagsFromNative(
diff --git a/ui/file_manager/video_player/css/header.css b/ui/file_manager/video_player/css/header.css
deleted file mode 100644
index 06c27b6..0000000
--- a/ui/file_manager/video_player/css/header.css
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Copyright 2014 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-#video-player > .header {
-  -webkit-app-region: drag;
-  background: rgba(30, 30, 30, 0.8);
-  border-bottom: 1px solid rgba(50, 50, 50, 0.8);
-  display: flex;
-  flex-direction: row;
-  height: 45px;
-  opacity: 1;
-  pointer-events: auto;
-  position: absolute;
-  top: 0;
-  width: 100%;
-  z-index: 200;
-}
-
-#video-player > .header > #title {
-  color: white;
-  flex-grow: 1;
-  font-size: 16px;
-  line-height: 45px;
-  overflow: hidden;
-  padding-left: 15px;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-
-#video-player > .header > button,
-#video-player > .header > button[disabled] {
-  align-content: center;
-  background-color: rgba(0, 0, 0, 0);
-  background-position: center;
-  background-repeat: no-repeat;
-  border: none;
-  box-shadow: none;
-  color: white;
-  cursor: pointer;
-  padding: 1px;  /* Instead of a border. */
-  position: relative;
-  z-index: 10;
-}
-
-#video-player > .header > button {
-  height: 40px;
-  margin: 6px 0;
-  min-width: 40px;  /* Reset. */
-  width: 40px;
-}
-
-#video-player > .header > button:hover {
-  background-color: rgba(240, 240, 240, 1);
-}
-
-#video-player > .header > button:active,
-#video-player > .header > button[pressed],
-#video-player > .header > button[pressed]:hover {
-  color: black;
-}
-
-#video-player > .header > button {
-  -webkit-app-region: no-drag;
-  -webkit-margin-end: 10px;
-  cursor: default;
-  height: 32px;
-  min-width: 32px;
-  width: 32px;
-}
-
-#video-player > .header > button.minimize-button {
-  background-image: -webkit-image-set(
-      url(chrome://resources/images/apps/topbar_button_minimize.png) 1x,
-      url(chrome://resources/images/2x/apps/topbar_button_minimize.png) 2x);
-}
-
-#video-player > .header > button.maximize-button {
-  background-image: -webkit-image-set(
-      url(chrome://resources/images/apps/topbar_button_maximize.png) 1x,
-      url(chrome://resources/images/2x/apps/topbar_button_maximize.png) 2x);
-}
-
-#video-player > .header > button.close-button {
-  background-image: -webkit-image-set(
-      url(chrome://resources/images/apps/topbar_button_close.png) 1x,
-      url(chrome://resources/images/2x/apps/topbar_button_close.png) 2x);
-}
diff --git a/ui/file_manager/video_player/images/icon/video-player-128.png b/ui/file_manager/video_player/images/icon/video-player-128.png
index 4ba890e..bca680d 100644
--- a/ui/file_manager/video_player/images/icon/video-player-128.png
+++ b/ui/file_manager/video_player/images/icon/video-player-128.png
Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-256.png b/ui/file_manager/video_player/images/icon/video-player-256.png
index b6b3d3f..669ca83 100644
--- a/ui/file_manager/video_player/images/icon/video-player-256.png
+++ b/ui/file_manager/video_player/images/icon/video-player-256.png
Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-32.png b/ui/file_manager/video_player/images/icon/video-player-32.png
index e5ac0d6..31d8e21 100644
--- a/ui/file_manager/video_player/images/icon/video-player-32.png
+++ b/ui/file_manager/video_player/images/icon/video-player-32.png
Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-48.png b/ui/file_manager/video_player/images/icon/video-player-48.png
index f3c342d7..ce573b3 100644
--- a/ui/file_manager/video_player/images/icon/video-player-48.png
+++ b/ui/file_manager/video_player/images/icon/video-player-48.png
Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-64.png b/ui/file_manager/video_player/images/icon/video-player-64.png
index 0e722b69..286e725 100644
--- a/ui/file_manager/video_player/images/icon/video-player-64.png
+++ b/ui/file_manager/video_player/images/icon/video-player-64.png
Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-96.png b/ui/file_manager/video_player/images/icon/video-player-96.png
index fc2b5bd..e757329 100644
--- a/ui/file_manager/video_player/images/icon/video-player-96.png
+++ b/ui/file_manager/video_player/images/icon/video-player-96.png
Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-favicon-16.png b/ui/file_manager/video_player/images/icon/video-player-favicon-16.png
index b0ab880..4a5bee1 100644
--- a/ui/file_manager/video_player/images/icon/video-player-favicon-16.png
+++ b/ui/file_manager/video_player/images/icon/video-player-favicon-16.png
Binary files differ
diff --git a/ui/file_manager/video_player/images/icon/video-player-favicon-32.png b/ui/file_manager/video_player/images/icon/video-player-favicon-32.png
index 5168798..53927bd 100644
--- a/ui/file_manager/video_player/images/icon/video-player-favicon-32.png
+++ b/ui/file_manager/video_player/images/icon/video-player-favicon-32.png
Binary files differ
diff --git a/ui/file_manager/video_player/js/background.js b/ui/file_manager/video_player/js/background.js
index 3b7adacb..3df0cdb1 100644
--- a/ui/file_manager/video_player/js/background.js
+++ b/ui/file_manager/video_player/js/background.js
@@ -17,7 +17,9 @@
  * @type {Object}
  */
 var windowCreateOptions = {
-  frame: 'none',
+  frame: {
+    color: '#fafafa'
+  },
   minWidth: 480,
   minHeight: 270
 };
diff --git a/ui/file_manager/video_player/js/video_player.js b/ui/file_manager/video_player/js/video_player.js
index dff5801..a9c39133 100644
--- a/ui/file_manager/video_player/js/video_player.js
+++ b/ui/file_manager/video_player/js/video_player.js
@@ -218,37 +218,6 @@
 
   document.ondragstart = preventDefault;
 
-  var maximizeButton = queryRequiredElement('.maximize-button');
-  maximizeButton.addEventListener(
-      'click',
-      function(event) {
-        var appWindow = chrome.app.window.current();
-        if (appWindow.isMaximized())
-          appWindow.restore();
-        else
-          appWindow.maximize();
-        event.stopPropagation();
-      }.wrap(null));
-  maximizeButton.addEventListener('mousedown', preventDefault);
-
-  var minimizeButton = queryRequiredElement('.minimize-button');
-  minimizeButton.addEventListener(
-      'click',
-      function(event) {
-        chrome.app.window.current().minimize();
-        event.stopPropagation();
-      }.wrap(null));
-  minimizeButton.addEventListener('mousedown', preventDefault);
-
-  var closeButton = queryRequiredElement('.close-button');
-  closeButton.addEventListener(
-      'click',
-      function(event) {
-        window.close();
-        event.stopPropagation();
-      }.wrap(null));
-  closeButton.addEventListener('mousedown', preventDefault);
-
   cr.ui.decorate(getRequiredElement('cast-menu'), cr.ui.Menu);
 
   this.controls_ = new FullWindowVideoControls(
@@ -308,8 +277,6 @@
   this.loadQueue_.run(function(callback) {
     document.title = video.name;
 
-    getRequiredElement('title').innerText = video.name;
-
     var videoPlayerElement = getRequiredElement('video-player');
     if (this.currentPos_ === (this.videos_.length - 1))
       videoPlayerElement.setAttribute('last-video', true);
diff --git a/ui/file_manager/video_player/video_player.html b/ui/file_manager/video_player/video_player.html
index ede9ad0..ad3fcb3c 100644
--- a/ui/file_manager/video_player/video_player.html
+++ b/ui/file_manager/video_player/video_player.html
@@ -13,7 +13,6 @@
   <link rel="stylesheet" href="chrome://resources/css/menu.css">
 
   <link rel="stylesheet" type="text/css" href="css/video_player.css">
-  <link rel="stylesheet" type="text/css" href="css/header.css">
   <link rel="stylesheet" type="text/css" href="css/arrow_box.css">
   <link rel="stylesheet" type="text/css" href="css/cast_menu.css">
 
@@ -36,12 +35,6 @@
     <div id="spinner-container">
       <div class="spinner"></div>
     </div>
-    <div id="header-container" class="header tool">
-      <div id="title">&nbsp;</div>
-      <button class="minimize-button tool" tabindex="-1"></button>
-      <button class="maximize-button tool" tabindex="-1"></button>
-      <button class="close-button tool" tabindex="-1"></button>
-    </div>
     <div id="controls-wrapper">
       <div id="controls" class="tool"></div>
     </div>
diff --git a/ui/keyboard/keyboard_util.cc b/ui/keyboard/keyboard_util.cc
index 2a5ae6a..4d2cefa 100644
--- a/ui/keyboard/keyboard_util.cc
+++ b/ui/keyboard/keyboard_util.cc
@@ -319,7 +319,9 @@
       ui::TextInputClient* tic = input_method->GetTextInputClient();
 
       SendProcessKeyEvent(ui::ET_KEY_PRESSED, host);
-      tic->InsertChar(static_cast<uint16>(key_value), ui::EF_NONE);
+
+      ui::KeyEvent char_event(key_value, code, ui::EF_NONE);
+      tic->InsertChar(char_event);
       SendProcessKeyEvent(ui::ET_KEY_RELEASED, host);
     }
   } else {
diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm
index 43a95a8..c30e7c7 100644
--- a/ui/views/cocoa/bridged_content_view.mm
+++ b/ui/views/cocoa/bridged_content_view.mm
@@ -639,10 +639,14 @@
   // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in
   // |text| but sending 'Alt' to InsertChar would filter it out since it thinks
   // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:.
-  if (inKeyDown_ && [text length] == 1)
-    textInputClient_->InsertChar([text characterAtIndex:0], 0);
-  else
+  if (inKeyDown_ && [text length] == 1) {
+    ui::KeyEvent char_event(
+        [text characterAtIndex:0],
+        static_cast<ui::KeyboardCode>([text characterAtIndex:0]), ui::EF_NONE);
+    textInputClient_->InsertChar(char_event);
+  } else {
     textInputClient_->InsertText(base::SysNSStringToUTF16(text));
+  }
 }
 
 - (NSRange)markedRange {
diff --git a/ui/views/controls/combobox/combobox_unittest.cc b/ui/views/controls/combobox/combobox_unittest.cc
index 0161c12..0d1211c 100644
--- a/ui/views/controls/combobox/combobox_unittest.cc
+++ b/ui/views/controls/combobox/combobox_unittest.cc
@@ -15,6 +15,7 @@
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/event_utils.h"
+#include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "ui/views/test/combobox_test_api.h"
@@ -695,13 +696,19 @@
       widget_->GetInputMethod()->GetTextInputClient();
 
   // Type the first character of the second menu item ("JELLY").
-  input_client->InsertChar('J', ui::EF_NONE);
+  ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_J, ui::DomCode::KEY_J, 0,
+                         ui::DomKey::FromCharacter('J'), ui::EventTimeForNow());
+
+  input_client->InsertChar(key_event);
   EXPECT_EQ(1, listener.actions_performed());
   EXPECT_EQ(1, listener.perform_action_index());
 
   // Type the second character of "JELLY", item shouldn't change and
   // OnPerformAction() shouldn't be re-called.
-  input_client->InsertChar('E', ui::EF_NONE);
+  key_event =
+      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_E, ui::DomCode::KEY_E, 0,
+                   ui::DomKey::FromCharacter('E'), ui::EventTimeForNow());
+  input_client->InsertChar(key_event);
   EXPECT_EQ(1, listener.actions_performed());
   EXPECT_EQ(1, listener.perform_action_index());
 
@@ -711,7 +718,10 @@
 
   // Type the first character of "PEANUT BUTTER", which should change the
   // selected index and perform an action.
-  input_client->InsertChar('P', ui::EF_NONE);
+  key_event =
+      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_E, ui::DomCode::KEY_P, 0,
+                   ui::DomKey::FromCharacter('P'), ui::EventTimeForNow());
+  input_client->InsertChar(key_event);
   EXPECT_EQ(2, listener.actions_performed());
   EXPECT_EQ(2, listener.perform_action_index());
 }
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index cd7fd41..b3a6ada 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -2209,7 +2209,7 @@
   }
 
   if (is_combobox_) {
-    item->GetSubmenu()->GetPrefixSelector()->InsertChar(character, 0);
+    item->GetSubmenu()->GetPrefixSelector()->InsertText(char_array);
   } else {
     // If no mnemonics found, look at first character of titles.
     details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic);
diff --git a/ui/views/controls/prefix_selector.cc b/ui/views/controls/prefix_selector.cc
index 9b84cfa..723e0e40 100644
--- a/ui/views/controls/prefix_selector.cc
+++ b/ui/views/controls/prefix_selector.cc
@@ -52,8 +52,8 @@
   OnTextInput(text);
 }
 
-void PrefixSelector::InsertChar(base::char16 ch, int flags) {
-  OnTextInput(base::string16(1, ch));
+void PrefixSelector::InsertChar(const ui::KeyEvent& event) {
+  OnTextInput(base::string16(1, event.GetCharacter()));
 }
 
 ui::TextInputType PrefixSelector::GetTextInputType() const {
diff --git a/ui/views/controls/prefix_selector.h b/ui/views/controls/prefix_selector.h
index c3dcc369..2b3988bb 100644
--- a/ui/views/controls/prefix_selector.h
+++ b/ui/views/controls/prefix_selector.h
@@ -29,7 +29,7 @@
   void ConfirmCompositionText() override;
   void ClearCompositionText() override;
   void InsertText(const base::string16& text) override;
-  void InsertChar(base::char16 ch, int flags) override;
+  void InsertChar(const ui::KeyEvent& event) override;
   ui::TextInputType GetTextInputType() const override;
   ui::TextInputMode GetTextInputMode() const override;
   int GetTextInputFlags() const override;
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index e11410b..039102b7 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -1450,14 +1450,15 @@
   OnAfterUserAction();
 }
 
-void Textfield::InsertChar(base::char16 ch, int flags) {
+void Textfield::InsertChar(const ui::KeyEvent& event) {
   // Filter out all control characters, including tab and new line characters,
   // and all characters with Alt modifier (and Search on ChromeOS). But allow
   // characters with the AltGr modifier. On Windows AltGr is represented by
   // Alt+Ctrl or Right Alt, and on Linux it's a different flag that we don't
   // care about.
+  const base::char16 ch = event.GetCharacter();
   const bool should_insert_char = ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) &&
-                                  !ui::IsSystemKeyModifier(flags);
+                                  !ui::IsSystemKeyModifier(event.flags());
   if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char)
     return;
 
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index fc61858..2cac671 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -284,7 +284,7 @@
   void ConfirmCompositionText() override;
   void ClearCompositionText() override;
   void InsertText(const base::string16& text) override;
-  void InsertChar(base::char16 ch, int flags) override;
+  void InsertChar(const ui::KeyEvent& event) override;
   ui::TextInputType GetTextInputType() const override;
   ui::TextInputMode GetTextInputMode() const override;
   int GetTextInputFlags() const override;
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 31748de..5e6e121 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -172,7 +172,7 @@
     } else if (key->type() == ui::ET_KEY_PRESSED) {
       base::char16 ch = key->GetCharacter();
       if (ch)
-        client->InsertChar(ch, key->flags());
+        client->InsertChar(*key);
     }
   }
 
@@ -272,8 +272,8 @@
   }
 
   // ui::TextInputClient overrides:
-  void InsertChar(base::char16 ch, int flags) override {
-    views::Textfield::InsertChar(ch, flags);
+  void InsertChar(const ui::KeyEvent& e) override {
+    views::Textfield::InsertChar(e);
 #if defined(OS_MACOSX)
     // On Mac, characters are inserted directly rather than attempting to get a
     // unicode character from the ui::KeyEvent (which isn't always possible).
diff --git a/ui/views/mus/input_method_mus.cc b/ui/views/mus/input_method_mus.cc
index 05c8c72ce..a7222dd 100644
--- a/ui/views/mus/input_method_mus.cc
+++ b/ui/views/mus/input_method_mus.cc
@@ -56,7 +56,7 @@
   // character event, we instead check to see if this is a character event and
   // send out the key if it is. (We fallback to normal dispatch if it isn't.)
   if (event->is_char()) {
-    GetTextInputClient()->InsertChar(event->GetCharacter(), event->flags());
+    GetTextInputClient()->InsertChar(*event);
     event->StopPropagation();
     return;
   }