diff --git a/AUTHORS b/AUTHORS
index 712cbde..233d94eb 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -671,6 +671,7 @@
 Sujae Jo <sujae33.jo@gmail.com>
 Sujith S S <sujiths.s@samsung.com>
 Sungguk Lim <limasdf@gmail.com>
+Sunghoon Kim <shoon.kim@lge.com>
 Sungmann Cho <sungmann.cho@gmail.com>
 Sungmann Cho <sungmann.cho@navercorp.com>
 Sunitha Srivatsa <srivats@amazon.com>
diff --git a/DEPS b/DEPS
index ab1b1e2e..923ce45 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'ffd7660a65e8470bf7df9141d281d75dcb359c45',
+  'v8_revision': '783b1c5d041c3c612c5193a45937c060a32cc0d0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '95b3e83b284d07b44518378ad795f15287249b19',
+  'catapult_revision': '201f910a0958c2c25481dc011616aada3df330d9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -228,7 +228,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '1638114289eca6e17cc412205c2970b2f6c98280', # commit position 16045
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'cda50c31ad6f31dfaa8568f6e23037dabb6b19e5', # commit position 16087
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
diff --git a/chrome/android/java/res/drawable-hdpi/ntp_signin_promo_card_bottom.9.png b/chrome/android/java/res/drawable-hdpi/ntp_signin_promo_card_bottom.9.png
deleted file mode 100644
index ea17ae5..0000000
--- a/chrome/android/java/res/drawable-hdpi/ntp_signin_promo_card_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ntp_signin_promo_card_bottom.9.png b/chrome/android/java/res/drawable-mdpi/ntp_signin_promo_card_bottom.9.png
deleted file mode 100644
index 191f356..0000000
--- a/chrome/android/java/res/drawable-mdpi/ntp_signin_promo_card_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ntp_signin_promo_card_bottom.9.png b/chrome/android/java/res/drawable-xhdpi/ntp_signin_promo_card_bottom.9.png
deleted file mode 100644
index 566a5cc..0000000
--- a/chrome/android/java/res/drawable-xhdpi/ntp_signin_promo_card_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ntp_signin_promo_card_bottom.9.png b/chrome/android/java/res/drawable-xxhdpi/ntp_signin_promo_card_bottom.9.png
deleted file mode 100644
index 05c5152..0000000
--- a/chrome/android/java/res/drawable-xxhdpi/ntp_signin_promo_card_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ntp_signin_promo_card_bottom.9.png b/chrome/android/java/res/drawable-xxxhdpi/ntp_signin_promo_card_bottom.9.png
deleted file mode 100644
index 9bef7de..0000000
--- a/chrome/android/java/res/drawable-xxxhdpi/ntp_signin_promo_card_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java
index bd45b9d..444be22d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java
@@ -80,7 +80,7 @@
     // The last image src for download, used for avoiding fetching the same src when artwork is set
     // multiple times but the same src is chosen.
     //
-    // Will be reset when initiating a new download request, and set to |null| when download failed.
+    // Will be reset when initiating a new download request.
     private String mLastImageSrc;
 
     /**
@@ -134,7 +134,9 @@
         mCallback = callback;
         MediaImage image = selectImage(images);
         if (image == null) {
-            onDownloadFailed();
+            mLastImageSrc = null;
+            mCallback.onImageDownloaded(null);
+            clearRequests();
             return;
         }
 
@@ -178,12 +180,8 @@
                 bestScore = newScore;
             }
         }
-        if (bestBitmap != null) {
-            mCallback.onImageDownloaded(bestBitmap);
-            clearRequests();
-        } else {
-            onDownloadFailed();
-        }
+        mCallback.onImageDownloaded(bestBitmap);
+        clearRequests();
     }
 
     /**
@@ -210,12 +208,6 @@
         mCallback = null;
     }
 
-    private void onDownloadFailed() {
-        mLastImageSrc = null;
-        mCallback.onImageDownloaded(null);
-        clearRequests();
-    }
-
     private double getImageScore(MediaImage image) {
         if (image == null) return 0;
         if (image.getSizes().isEmpty()) return DEFAULT_IMAGE_SIZE_SCORE;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
index e29abda9..41b37ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardViewHolder.java
@@ -171,8 +171,13 @@
         int abovePosition = getAdapterPosition() - 1;
         boolean hasCardAbove = abovePosition >= 0 && isCard(adapter.getItemViewType(abovePosition));
         int belowPosition = getAdapterPosition() + 1;
-        boolean hasCardBelow = belowPosition < adapter.getItemCount()
-                && isCard(adapter.getItemViewType(belowPosition));
+        boolean hasCardBelow = false;
+        if (belowPosition < adapter.getItemCount()) {
+            // The PROMO card has an empty margin and will not be right against the preceding card,
+            // so we don't consider it a card from the point of view of the preceding one.
+            @ItemViewType int belowViewType = adapter.getItemViewType(belowPosition);
+            hasCardBelow = isCard(belowViewType) && belowViewType != ItemViewType.PROMO;
+        }
 
         getParams().bottomMargin = hasCardBelow ? -mCards9PatchAdjustment : 0;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java
index 15a6ffe..0a31854 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java
@@ -8,7 +8,6 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
-import android.support.v7.widget.RecyclerView;
 
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
@@ -176,46 +175,21 @@
      * View Holder for {@link SignInPromo}.
      */
     public static class ViewHolder extends StatusCardViewHolder {
-        private final int mSeparationSpaceSize;
 
         public ViewHolder(NewTabPageRecyclerView parent, NewTabPageManager newTabPageManager,
                 UiConfig config) {
             super(parent, newTabPageManager, config);
-            mSeparationSpaceSize = parent.getResources().getDimensionPixelSize(
+            getParams().topMargin = parent.getResources().getDimensionPixelSize(
                     R.dimen.ntp_sign_in_promo_margin_top);
         }
 
         @DrawableRes
         @Override
         protected int selectBackground(boolean hasCardAbove, boolean hasCardBelow) {
-            assert !hasCardBelow;
-            if (hasCardAbove) return R.drawable.ntp_signin_promo_card_bottom;
             return R.drawable.ntp_signin_promo_card_single;
         }
 
         @Override
-        public void updateLayoutParams() {
-            super.updateLayoutParams();
-
-            if (getAdapterPosition() == RecyclerView.NO_POSITION) return;
-
-            int precedingPosition = getAdapterPosition() - 1;
-            if (precedingPosition < 0) return; // Invalid adapter position, just do nothing.
-
-            @ItemViewType
-            int precedingCardType =
-                    getRecyclerView().getAdapter().getItemViewType(precedingPosition);
-
-            // The sign in promo should stick to the articles of the preceding section, but have
-            // some space otherwise.
-            if (precedingCardType != ItemViewType.SNIPPET) {
-                getParams().topMargin = mSeparationSpaceSize;
-            } else {
-                getParams().topMargin = 0;
-            }
-        }
-
-        @Override
         public boolean isDismissable() {
             return true;
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaImageManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaImageManagerTest.java
index 372dd01..ce176cf 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaImageManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaImageManagerTest.java
@@ -120,6 +120,30 @@
     }
 
     @Test
+    public void testDownloadSameImageTwiceButFailed() {
+        // First download.
+        mBitmaps.clear();
+        mOriginalImageSizes.clear();
+
+        mMediaImageManager.downloadImage(mImages, mCallback);
+        mMediaImageManager.onFinishDownloadImage(
+                REQUEST_ID_1, 404, IMAGE_URL_1, mBitmaps, mOriginalImageSizes);
+
+        // Second download.
+        mMediaImageManager.downloadImage(mImages, mCallback);
+        // The second download request will never be initiated and the callback
+        // will be ignored.
+        mMediaImageManager.onFinishDownloadImage(
+                REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes);
+
+        verify(mWebContents, times(1))
+                .downloadImage(eq(IMAGE_URL_1), eq(false),
+                        eq(MediaImageManager.MAX_BITMAP_SIZE_FOR_DOWNLOAD), eq(false),
+                        eq(mMediaImageManager));
+        verify(mCallback, times(1)).onImageDownloaded(isNull(Bitmap.class));
+    }
+
+    @Test
     public void testDownloadDifferentImagesTwice() {
         // First download.
         mMediaImageManager.downloadImage(mImages, mCallback);
diff --git a/chrome/browser/budget_service/budget_database.cc b/chrome/browser/budget_service/budget_database.cc
index 28e3f7eb..662c2b94 100644
--- a/chrome/browser/budget_service/budget_database.cc
+++ b/chrome/browser/budget_service/budget_database.cc
@@ -27,8 +27,13 @@
 const char kDatabaseUMAName[] = "BudgetManager";
 
 // The default amount of time during which a budget will be valid.
-// This is 4 days = 96 hours.
-constexpr double kBudgetDurationInHours = 96;
+constexpr int kBudgetDurationInDays = 4;
+
+// The amount of budget that a maximally engaged site should receive per hour.
+// For context, silent push messages cost 2 each, so this allows 6 silent push
+// messages a day for a fully engaged site. See budget_manager.cc for costs of
+// various actions.
+constexpr double kMaximumHourlyBudget = 12.0 / 24.0;
 
 }  // namespace
 
@@ -164,8 +169,6 @@
     predictions.push_back(std::move(prediction));
   }
 
-  DCHECK_EQ(0, total);
-
   callback.Run(blink::mojom::BudgetServiceErrorType::NONE,
                std::move(predictions));
 }
@@ -304,35 +307,36 @@
 }
 
 void BudgetDatabase::AddEngagementBudget(const url::Origin& origin) {
-  // Get the current SES score, which we'll use to set a new budget.
-  SiteEngagementService* service = SiteEngagementService::Get(profile_);
-  double score = service->GetScore(origin.GetURL());
-
-  // By default we award the "full" award. Then that ratio is decreased if
-  // there have been other awards recently.
-  double ratio = 1.0;
-
-  // Calculate how much budget should be awarded. If there is no entry in the
-  // cache then we award a full amount.
+  // Calculate how much budget should be awarded. The award depends on the
+  // time elapsed since the last award and the SES score.
+  // By default, give the origin kBudgetDurationInDays worth of budget, but
+  // reduce that if budget has already been given during that period.
+  base::TimeDelta elapsed = base::TimeDelta::FromDays(kBudgetDurationInDays);
   if (IsCached(origin)) {
-    base::TimeDelta elapsed =
-        clock_->Now() - budget_map_[origin].last_engagement_award;
-    int elapsed_hours = elapsed.InHours();
+    elapsed = clock_->Now() - budget_map_[origin].last_engagement_award;
     // Don't give engagement awards for periods less than an hour.
-    if (elapsed_hours < 1)
+    if (elapsed.InHours() < 1)
       return;
-    if (elapsed_hours < kBudgetDurationInHours)
-      ratio = elapsed_hours / kBudgetDurationInHours;
+    // Cap elapsed time to the budget duration.
+    if (elapsed.InDays() > kBudgetDurationInDays)
+      elapsed = base::TimeDelta::FromDays(kBudgetDurationInDays);
   }
 
+  // Get the current SES score, and calculate the hourly budget for that score.
+  SiteEngagementService* service = SiteEngagementService::Get(profile_);
+  double hourly_budget = kMaximumHourlyBudget *
+                         service->GetScore(origin.GetURL()) /
+                         service->GetMaxPoints();
+
   // Update the last_engagement_award to the current time. If the origin wasn't
   // already in the map, this adds a new entry for it.
   budget_map_[origin].last_engagement_award = clock_->Now();
 
   // Add a new chunk of budget for the origin at the default expiration time.
   base::Time expiration =
-      clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours);
-  budget_map_[origin].chunks.emplace_back(ratio * score, expiration);
+      clock_->Now() + base::TimeDelta::FromDays(kBudgetDurationInDays);
+  budget_map_[origin].chunks.emplace_back(elapsed.InHours() * hourly_budget,
+                                          expiration);
 
   // Any time we award engagement budget, which is done at most once an hour
   // whenever any budget action is taken, record the budget.
@@ -355,10 +359,10 @@
     cleanup_iter = chunks.erase(cleanup_iter);
 
   // If the entire budget is empty now AND there have been no engagements
-  // in the last kBudgetDurationInHours hours, remove this from the cache.
+  // in the last kBudgetDurationInDays days, remove this from the cache.
   if (chunks.empty() &&
       budget_map_[origin].last_engagement_award <
-          clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) {
+          clock_->Now() - base::TimeDelta::FromDays(kBudgetDurationInDays)) {
     budget_map_.erase(origin);
     return true;
   }
diff --git a/chrome/browser/budget_service/budget_database_unittest.cc b/chrome/browser/budget_service/budget_database_unittest.cc
index d25c93f..1f942b78 100644
--- a/chrome/browser/budget_service/budget_database_unittest.cc
+++ b/chrome/browser/budget_service/budget_database_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/budget_service/budget_database.h"
 
+#include <math.h>
 #include <vector>
 
 #include "base/memory/ptr_util.h"
@@ -12,6 +13,7 @@
 #include "base/test/simple_test_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/budget_service/budget.pb.h"
+#include "chrome/browser/engagement/site_engagement_score.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/leveldb_proto/proto_database.h"
@@ -24,8 +26,11 @@
 
 namespace {
 
-const double kDefaultExpirationInHours = 96;
-const double kDefaultEngagement = 30.0;
+// These values mirror the defaults in budget_database.cc
+const double kDefaultExpirationInDays = 4;
+const double kMaxDailyBudget = 12;
+
+const double kEngagement = 25;
 
 const char kTestOrigin[] = "https://example.com";
 
@@ -114,16 +119,20 @@
 TEST_F(BudgetDatabaseTest, AddEngagementBudgetTest) {
   base::SimpleTestClock* clock = SetClockForTesting();
   base::Time expiration_time =
-      clock->Now() + base::TimeDelta::FromHours(kDefaultExpirationInHours);
+      clock->Now() + base::TimeDelta::FromDays(kDefaultExpirationInDays);
 
   // Set the default site engagement.
-  SetSiteEngagementScore(kDefaultEngagement);
+  SetSiteEngagementScore(kEngagement);
 
-  // The budget should include a full share of the engagement.
+  // The budget should include kDefaultExpirationInDays days worth of
+  // engagement.
+  double daily_budget =
+      kMaxDailyBudget * (kEngagement / SiteEngagementScore::kMaxPoints);
   GetBudgetDetails();
   ASSERT_TRUE(success_);
   ASSERT_EQ(2U, prediction_.size());
-  ASSERT_EQ(kDefaultEngagement, prediction_[0]->budget_at);
+  ASSERT_DOUBLE_EQ(daily_budget * kDefaultExpirationInDays,
+                   prediction_[0]->budget_at);
   ASSERT_EQ(0, prediction_[1]->budget_at);
   ASSERT_EQ(expiration_time.ToDoubleT(), prediction_[1]->time);
 
@@ -134,12 +143,11 @@
   // The budget should now have 1 full share plus 1 daily budget.
   ASSERT_TRUE(success_);
   ASSERT_EQ(3U, prediction_.size());
-  double daily_budget = kDefaultEngagement * 24 / kDefaultExpirationInHours;
-  ASSERT_DOUBLE_EQ(kDefaultEngagement + daily_budget,
+  ASSERT_DOUBLE_EQ(daily_budget * (kDefaultExpirationInDays + 1),
                    prediction_[0]->budget_at);
   ASSERT_DOUBLE_EQ(daily_budget, prediction_[1]->budget_at);
   ASSERT_EQ(expiration_time.ToDoubleT(), prediction_[1]->time);
-  ASSERT_EQ(0, prediction_[2]->budget_at);
+  ASSERT_DOUBLE_EQ(0, prediction_[2]->budget_at);
   ASSERT_EQ((expiration_time + base::TimeDelta::FromDays(1)).ToDoubleT(),
             prediction_[2]->time);
 
@@ -151,7 +159,7 @@
   // The budget should be the same as before the attempted add.
   ASSERT_TRUE(success_);
   ASSERT_EQ(3U, prediction_.size());
-  ASSERT_DOUBLE_EQ(kDefaultEngagement + daily_budget,
+  ASSERT_DOUBLE_EQ(daily_budget * (kDefaultExpirationInDays + 1),
                    prediction_[0]->budget_at);
 }
 
@@ -159,7 +167,7 @@
   base::SimpleTestClock* clock = SetClockForTesting();
 
   // Set the default site engagement.
-  SetSiteEngagementScore(kDefaultEngagement);
+  SetSiteEngagementScore(kEngagement);
 
   // Intialize the budget with several chunks.
   GetBudgetDetails();
@@ -168,15 +176,16 @@
   clock->Advance(base::TimeDelta::FromDays(1));
   GetBudgetDetails();
 
-  // Spend an amount of budget less than kDefaultEngagement.
+  // Spend an amount of budget less than the daily budget.
   ASSERT_TRUE(SpendBudget(1));
   GetBudgetDetails();
 
-  // There should still be three chunks of budget of size kDefaultEngagement-1,
-  // kDefaultEngagement, and kDefaultEngagement.
+  // There should still be three chunks of budget of size daily_budget-1,
+  // daily_budget, and kDefaultExpirationInDays * daily_budget.
+  double daily_budget =
+      kMaxDailyBudget * (kEngagement / SiteEngagementScore::kMaxPoints);
   ASSERT_EQ(4U, prediction_.size());
-  double daily_budget = kDefaultEngagement * 24 / kDefaultExpirationInHours;
-  ASSERT_DOUBLE_EQ(kDefaultEngagement + 2 * daily_budget - 1,
+  ASSERT_DOUBLE_EQ((2 + kDefaultExpirationInDays) * daily_budget - 1,
                    prediction_[0]->budget_at);
   ASSERT_DOUBLE_EQ(daily_budget * 2, prediction_[1]->budget_at);
   ASSERT_DOUBLE_EQ(daily_budget, prediction_[2]->budget_at);
@@ -184,22 +193,22 @@
 
   // Now spend enough that it will use up the rest of the first chunk and all of
   // the second chunk, but not all of the third chunk.
-  ASSERT_TRUE(SpendBudget(kDefaultEngagement + daily_budget));
+  ASSERT_TRUE(SpendBudget((1 + kDefaultExpirationInDays) * daily_budget));
   GetBudgetDetails();
   ASSERT_EQ(2U, prediction_.size());
   ASSERT_DOUBLE_EQ(daily_budget - 1, prediction_[0]->budget_at);
 
   // Validate that the code returns false if SpendBudget tries to spend more
   // budget than the origin has.
-  EXPECT_FALSE(SpendBudget(kDefaultEngagement));
+  EXPECT_FALSE(SpendBudget(kEngagement));
   GetBudgetDetails();
   ASSERT_EQ(2U, prediction_.size());
   ASSERT_DOUBLE_EQ(daily_budget - 1, prediction_[0]->budget_at);
 
   // Advance time until the last remaining chunk should be expired, then query
   // for the full engagement worth of budget.
-  clock->Advance(base::TimeDelta::FromHours(kDefaultExpirationInHours + 1));
-  EXPECT_TRUE(SpendBudget(kDefaultEngagement));
+  clock->Advance(base::TimeDelta::FromDays(kDefaultExpirationInDays + 1));
+  EXPECT_TRUE(SpendBudget(daily_budget * kDefaultExpirationInDays));
 }
 
 // There are times when a device's clock could move backwards in time, either
@@ -210,7 +219,7 @@
   base::SimpleTestClock* clock = SetClockForTesting();
 
   // Set the default site engagement.
-  SetSiteEngagementScore(kDefaultEngagement);
+  SetSiteEngagementScore(kEngagement);
 
   // Initialize the budget with two chunks.
   GetBudgetDetails();
@@ -241,17 +250,17 @@
   base::SimpleTestClock* clock = SetClockForTesting();
 
   // Set the default site engagement.
-  SetSiteEngagementScore(kDefaultEngagement);
+  SetSiteEngagementScore(kEngagement);
 
   // Initialize the budget with some interesting chunks: 30 budget (full
   // engagement), 15 budget (half of the engagement), 0 budget (less than an
   // hour), and then after the first two expire, another 30 budget.
   GetBudgetDetails();
-  clock->Advance(base::TimeDelta::FromHours(kDefaultExpirationInHours / 2));
+  clock->Advance(base::TimeDelta::FromDays(kDefaultExpirationInDays / 2));
   GetBudgetDetails();
   clock->Advance(base::TimeDelta::FromMinutes(59));
   GetBudgetDetails();
-  clock->Advance(base::TimeDelta::FromHours(kDefaultExpirationInHours + 1));
+  clock->Advance(base::TimeDelta::FromDays(kDefaultExpirationInDays + 1));
   GetBudgetDetails();
 
   // The BackgroundBudget UMA is recorded when budget is added to the origin.
@@ -259,20 +268,25 @@
   std::vector<base::Bucket> buckets =
       GetHistogramTester()->GetAllSamples("PushMessaging.BackgroundBudget");
   ASSERT_EQ(2U, buckets.size());
-  // First bucket is for full engagement, which should have 2 entries.
-  EXPECT_EQ(kDefaultEngagement, buckets[0].min);
+  // First bucket is for full award, which should have 2 entries.
+  double full_award = kMaxDailyBudget * kEngagement /
+                      SiteEngagementScore::kMaxPoints *
+                      kDefaultExpirationInDays;
+  EXPECT_EQ(floor(full_award), buckets[0].min);
   EXPECT_EQ(2, buckets[0].count);
-  // Second bucket is for 1.5 * engagement, which should have 1 entry.
-  EXPECT_EQ(kDefaultEngagement * 1.5, buckets[1].min);
+  // Second bucket is for 1.5 * award, which should have 1 entry.
+  EXPECT_EQ(floor(full_award * 1.5), buckets[1].min);
   EXPECT_EQ(1, buckets[1].count);
 }
 
 TEST_F(BudgetDatabaseTest, CheckEngagementHistograms) {
   base::SimpleTestClock* clock = SetClockForTesting();
 
-  // Set the engagement to twice the cost of an action.
+  // Manipulate the engagement so that the budget is twice the cost of an
+  // action.
   double cost = 2;
-  double engagement = cost * 2;
+  double engagement = 2 * cost * SiteEngagementScore::kMaxPoints /
+                      kDefaultExpirationInDays / kMaxDailyBudget;
   SetSiteEngagementScore(engagement);
 
   // Get the budget, which will award a chunk of budget equal to engagement.
@@ -299,17 +313,17 @@
   std::vector<base::Bucket> no_budget_buckets =
       GetHistogramTester()->GetAllSamples("PushMessaging.SESForNoBudgetOrigin");
   ASSERT_EQ(2U, no_budget_buckets.size());
-  EXPECT_EQ(engagement, no_budget_buckets[0].min);
+  EXPECT_EQ(floor(engagement), no_budget_buckets[0].min);
   EXPECT_EQ(1, no_budget_buckets[0].count);
-  EXPECT_EQ(engagement * 2, no_budget_buckets[1].min);
+  EXPECT_EQ(floor(engagement * 2), no_budget_buckets[1].min);
   EXPECT_EQ(1, no_budget_buckets[1].count);
 
   std::vector<base::Bucket> low_budget_buckets =
       GetHistogramTester()->GetAllSamples(
           "PushMessaging.SESForLowBudgetOrigin");
   ASSERT_EQ(2U, low_budget_buckets.size());
-  EXPECT_EQ(engagement, low_budget_buckets[0].min);
+  EXPECT_EQ(floor(engagement), low_budget_buckets[0].min);
   EXPECT_EQ(1, low_budget_buckets[0].count);
-  EXPECT_EQ(engagement * 2, low_budget_buckets[1].min);
+  EXPECT_EQ(floor(engagement * 2), low_budget_buckets[1].min);
   EXPECT_EQ(1, low_budget_buckets[1].count);
 }
diff --git a/chrome/browser/budget_service/budget_manager_browsertest.cc b/chrome/browser/budget_service/budget_manager_browsertest.cc
index aef3cd4..299abe21 100644
--- a/chrome/browser/budget_service/budget_manager_browsertest.cc
+++ b/chrome/browser/budget_service/budget_manager_browsertest.cc
@@ -8,6 +8,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/budget_service/budget_manager.h"
 #include "chrome/browser/budget_service/budget_manager_factory.h"
+#include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -19,6 +20,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/WebKit/public/platform/modules/budget_service/budget_service.mojom.h"
+#include "url/gurl.h"
 #include "url/origin.h"
 
 namespace {
@@ -55,6 +57,12 @@
     InProcessBrowserTest::SetUpCommandLine(command_line);
   }
 
+  void SetSiteEngagementScore(double score) {
+    SiteEngagementService* service =
+        SiteEngagementService::Get(browser()->profile());
+    service->ResetScoreForURL(https_server_->GetURL(kTestURL), score);
+  }
+
   bool RunScript(const std::string& script, std::string* result) {
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
@@ -94,20 +102,22 @@
 IN_PROC_BROWSER_TEST_F(BudgetManagerBrowserTest, BudgetInDocument) {
   std::string script_result;
 
-  // The page will have been loaded once, which gives a budget of 3.
+  SetSiteEngagementScore(5);
+
+  // Site Engagement score of 5 gives a budget of 2.
   ASSERT_TRUE(RunScript("documentGetBudget()", &script_result));
-  ASSERT_EQ("ok - budget returned value of 3", script_result);
+  EXPECT_EQ("ok - budget returned value of 2", script_result);
 
   ASSERT_TRUE(RunScript("documentReserveBudget()", &script_result));
-  ASSERT_EQ("ok - reserved budget", script_result);
+  EXPECT_EQ("ok - reserved budget", script_result);
 
-  // After reserving budget, the new budget should be at 1.
+  // After reserving budget, the new budget should be at 0.
   ASSERT_TRUE(RunScript("documentGetBudget()", &script_result));
-  ASSERT_EQ("ok - budget returned value of 1", script_result);
+  EXPECT_EQ("ok - budget returned value of 0", script_result);
 
   // A second reserve should fail because there is not enough budget.
   ASSERT_TRUE(RunScript("documentReserveBudget()", &script_result));
-  ASSERT_EQ("failed - not able to reserve budget", script_result);
+  EXPECT_EQ("failed - not able to reserve budget", script_result);
 
   // Consume should succeed because there is an existing reservation.
   ConsumeReservation();
@@ -125,29 +135,29 @@
   ASSERT_EQ("ok - service worker registered", script_result);
 
   LoadTestPage();  // Reload to become controlled.
+  SetSiteEngagementScore(12);
 
   ASSERT_TRUE(RunScript("isControlled()", &script_result));
   ASSERT_EQ("true - is controlled", script_result);
 
-  // The page will have been loaded twice and a service worker was registered,
-  // which gives a budget of 4.5.
+  // Site engagement score of 12 gives a budget of 5.
   ASSERT_TRUE(RunScript("workerGetBudget()", &script_result));
-  ASSERT_EQ("ok - budget returned value of 4.5", script_result);
+  EXPECT_EQ("ok - budget returned value of 5", script_result);
 
-  // With a budget of 4.5, two reservations should succeed.
+  // With a budget of 5, two reservations should succeed.
   ASSERT_TRUE(RunScript("workerReserveBudget()", &script_result));
-  ASSERT_EQ("ok - reserved budget", script_result);
+  EXPECT_EQ("ok - reserved budget", script_result);
 
   ASSERT_TRUE(RunScript("workerReserveBudget()", &script_result));
-  ASSERT_EQ("ok - reserved budget", script_result);
+  EXPECT_EQ("ok - reserved budget", script_result);
 
-  // After reserving budget, the new budget should be at 0.5.
+  // After reserving budget, the new budget should be at 1.
   ASSERT_TRUE(RunScript("workerGetBudget()", &script_result));
-  ASSERT_EQ("ok - budget returned value of 0.5", script_result);
+  EXPECT_EQ("ok - budget returned value of 1", script_result);
 
   // A second reserve should fail because there is not enough budget.
   ASSERT_TRUE(RunScript("workerReserveBudget()", &script_result));
-  ASSERT_EQ("failed - not able to reserve budget", script_result);
+  EXPECT_EQ("failed - not able to reserve budget", script_result);
 
   // Two consumes should succeed because there are existing reservations.
   ConsumeReservation();
diff --git a/chrome/browser/budget_service/budget_manager_unittest.cc b/chrome/browser/budget_service/budget_manager_unittest.cc
index 43b48f105..ab0802f 100644
--- a/chrome/browser/budget_service/budget_manager_unittest.cc
+++ b/chrome/browser/budget_service/budget_manager_unittest.cc
@@ -115,15 +115,15 @@
 };
 
 TEST_F(BudgetManagerTest, GetBudgetConsumedOverTime) {
-  // Set initial SES. The first time we try to spend budget, the
-  // engagement award will be granted which is 48.0.
+  // Set initial SES. The first time we try to spend budget, the engagement
+  // award will be granted which is 23.04. (kTestSES * maxDaily / maxSES)
   SetSiteEngagementScore(kTestSES);
   const blink::mojom::BudgetOperationType type =
       blink::mojom::BudgetOperationType::SILENT_PUSH;
 
-  // Spend for 24 silent push messages. This should consume all the original
+  // Spend for 11 silent push messages. This should consume all the original
   // budget grant.
-  for (int i = 0; i < 24; i++) {
+  for (int i = 0; i < 11; i++) {
     ASSERT_TRUE(GetBudget());
     ASSERT_TRUE(ReserveBudget(type));
   }
@@ -132,8 +132,8 @@
   ASSERT_TRUE(GetBudget());
   ASSERT_FALSE(ReserveBudget(type));
 
-  // Try to consume for the 24 messages reserved.
-  for (int i = 0; i < 24; i++)
+  // Try to consume for the 11 messages reserved.
+  for (int i = 0; i < 11; i++)
     ASSERT_TRUE(ConsumeBudget(type));
 
   // The next consume should fail, since there is no reservation or budget
@@ -148,9 +148,9 @@
   // 1 failed reserve call.
   EXPECT_EQ(0, buckets[0].min);
   EXPECT_EQ(1, buckets[0].count);
-  // 24 successful reserve calls.
+  // 11 successful reserve calls.
   EXPECT_EQ(1, buckets[1].min);
-  EXPECT_EQ(24, buckets[1].count);
+  EXPECT_EQ(11, buckets[1].count);
 
   // Check that the UMA recorded for the GetBudget calls matches the operations
   // that were executed.
@@ -159,7 +159,7 @@
   int num_samples = 0;
   for (const base::Bucket& bucket : buckets)
     num_samples += bucket.count;
-  EXPECT_EQ(25, num_samples);
+  EXPECT_EQ(12, num_samples);
 }
 
 TEST_F(BudgetManagerTest, TestInsecureOrigin) {
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 13b3c100..90a6a56 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -1240,9 +1240,10 @@
   content::WebContents* web_contents =
       GetBrowser()->tab_strip_model()->GetActiveWebContents();
 
-  // Set the site engagement score for the site. Setting it to 4 means it should
-  // have enough budget for two non-shown notification, which cost 2 each.
-  SetSiteEngagementScore(web_contents->GetURL(), 4.0);
+  // Set the site engagement score for the site. Setting it to 10 means it
+  // should have a budget of 4, enough for two non-shown notification, which
+  // cost 2 each.
+  SetSiteEngagementScore(web_contents->GetURL(), 10.0);
 
   // If the site is visible in an active tab, we should not force a notification
   // to be shown. Try it twice, since we allow one mistake per 10 push events.
diff --git a/chrome/test/data/webrtc/peerconnection_getstats.js b/chrome/test/data/webrtc/peerconnection_getstats.js
index 300ae02..6054a5c 100644
--- a/chrome/test/data/webrtc/peerconnection_getstats.js
+++ b/chrome/test/data/webrtc/peerconnection_getstats.js
@@ -21,6 +21,9 @@
   associateStatsId: 'string',
   isRemote: 'boolean',
   mediaType: 'string',
+  trackId: 'string',
+  // TODO(hbos): As soon as |mediaTrackId| has been renamed to |trackId|, remove
+  // this line. crbug.com/657854
   mediaTrackId: 'string',
   transportId: 'string',
   codecId: 'string',
@@ -93,6 +96,8 @@
 var kRTCPeerConnectionStats = new RTCStats_(null, {
   dataChannelsOpened: 'number',
   dataChannelsClosed: 'number',
+  dataChannelsRequested: 'number',
+  dataChannelsAccepted: 'number',
 });
 gStatsWhitelist.set('peer-connection', kRTCPeerConnectionStats);
 
@@ -117,6 +122,9 @@
   remoteSource: 'boolean',
   ended: 'boolean',
   detached: 'boolean',
+  kind: 'string',
+  // TODO(hbos): As soon as |ssrcIds| has been removed, remove this line.
+  // crbug.com/659137
   ssrcIds: 'sequence_string',
   frameWidth: 'number',
   frameHeight: 'number',
@@ -160,6 +168,9 @@
   bytesSent: 'number',
   bytesReceived: 'number',
   rtcpTransportStatsId: 'string',
+  dtlsState: 'string',
+  // TODO(hbos): As soon as |activeConnection| has been replaced by |dtlsState|,
+  // remove this line. crbug.com/653873
   activeConnection: 'boolean',
   selectedCandidatePairId: 'string',
   localCertificateId: 'string',
diff --git a/components/ntp_snippets_strings.grdp b/components/ntp_snippets_strings.grdp
index c3cfde7..e9e2ef5 100644
--- a/components/ntp_snippets_strings.grdp
+++ b/components/ntp_snippets_strings.grdp
@@ -5,8 +5,8 @@
     <message name="IDS_SNIPPETS_DISABLED_SIGNED_OUT_INSTRUCTIONS" desc="Body of the card explaining the status of the content suggestions on the New Tab Page, when the user is signed out." formatter_data="android_java">
       To get personalized content suggested by Google, sign in to Chrome.
     </message>
-    <message name="IDS_SNIPPETS_DISABLED_GENERIC_PROMPT" desc="Title of the card explaining what action the user can take to get content suggestions on the New Tab Page." formatter_data="android_java">
-      Get suggested content
+    <message name="IDS_SNIPPETS_DISABLED_GENERIC_PROMPT" desc="Title of the card explaining what action the user can take to get personalized content suggestions on the New Tab Page." formatter_data="android_java">
+      Get personalized content
     </message>
     <message name="IDS_NTP_STATUS_CARD_TITLE_NO_SUGGESTIONS" desc="On the New Tab Page, title of the status card explaining that there is no new content available in the section." formatter_data="android_java">
       That’s all for now
diff --git a/components/os_crypt/libsecret_util_linux.cc b/components/os_crypt/libsecret_util_linux.cc
index 611b0fed..b14bbb2e 100644
--- a/components/os_crypt/libsecret_util_linux.cc
+++ b/components/os_crypt/libsecret_util_linux.cc
@@ -19,7 +19,7 @@
 // create, to explain its existence.
 const char kExplanationMessage[] =
     "Because of quirks in the gnome libsecret API, Chrome needs to store a "
-    "dummy entry to quarantee that this keyring was properly unlocked. More "
+    "dummy entry to guarantee that this keyring was properly unlocked. More "
     "details at http://crbug.com/660005.";
 
 }  // namespace
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc
index e92dd9b..2f401c8c 100644
--- a/net/disk_cache/backend_unittest.cc
+++ b/net/disk_cache/backend_unittest.cc
@@ -126,6 +126,7 @@
   void BackendDoomRecent();
   void BackendDoomBetween();
   void BackendCalculateSizeOfAllEntries();
+  void BackendCalculateSizeOfEntriesBetween();
   void BackendTransaction(const std::string& name, int num_entries, bool load);
   void BackendRecoverInsert();
   void BackendRecoverRemove();
@@ -1872,6 +1873,70 @@
   BackendCalculateSizeOfAllEntries();
 }
 
+void DiskCacheBackendTest::BackendCalculateSizeOfEntriesBetween() {
+  InitCache();
+
+  EXPECT_EQ(0, CalculateSizeOfEntriesBetween(base::Time(), base::Time::Max()));
+
+  Time start = Time::Now();
+
+  disk_cache::Entry* entry;
+  ASSERT_THAT(CreateEntry("first", &entry), IsOk());
+  entry->Close();
+  FlushQueueForTest();
+
+  AddDelay();
+  Time middle = Time::Now();
+  AddDelay();
+
+  ASSERT_THAT(CreateEntry("second", &entry), IsOk());
+  entry->Close();
+  ASSERT_THAT(CreateEntry("third_entry", &entry), IsOk());
+  entry->Close();
+  FlushQueueForTest();
+
+  AddDelay();
+  Time end = Time::Now();
+
+  int size_1 = GetEntryMetadataSize("first");
+  int size_2 = GetEntryMetadataSize("second");
+  int size_3 = GetEntryMetadataSize("third_entry");
+
+  ASSERT_EQ(3, cache_->GetEntryCount());
+  ASSERT_EQ(CalculateSizeOfAllEntries(),
+            CalculateSizeOfEntriesBetween(base::Time(), base::Time::Max()));
+
+  int start_end = CalculateSizeOfEntriesBetween(start, end);
+  ASSERT_EQ(CalculateSizeOfAllEntries(), start_end);
+  ASSERT_EQ(size_1 + size_2 + size_3, start_end);
+
+  ASSERT_EQ(size_1, CalculateSizeOfEntriesBetween(start, middle));
+  ASSERT_EQ(size_2 + size_3, CalculateSizeOfEntriesBetween(middle, end));
+
+  // After dooming the entries, the size should be back to zero.
+  ASSERT_THAT(DoomAllEntries(), IsOk());
+  EXPECT_EQ(0, CalculateSizeOfEntriesBetween(base::Time(), base::Time::Max()));
+}
+
+TEST_F(DiskCacheBackendTest, CalculateSizeOfEntriesBetween) {
+  InitCache();
+  ASSERT_EQ(net::ERR_NOT_IMPLEMENTED,
+            CalculateSizeOfEntriesBetween(base::Time(), base::Time::Max()));
+}
+
+TEST_F(DiskCacheBackendTest, MemoryOnlyCalculateSizeOfEntriesBetween) {
+  SetMemoryOnlyMode();
+  BackendCalculateSizeOfEntriesBetween();
+}
+
+TEST_F(DiskCacheBackendTest, SimpleCacheCalculateSizeOfEntriesBetween) {
+  // Use net::APP_CACHE to make size estimations deterministic via
+  // non-optimistic writes.
+  SetCacheType(net::APP_CACHE);
+  SetSimpleCacheMode();
+  BackendCalculateSizeOfEntriesBetween();
+}
+
 void DiskCacheBackendTest::BackendTransaction(const std::string& name,
                                               int num_entries, bool load) {
   success_ = false;
diff --git a/net/disk_cache/cache_creator.cc b/net/disk_cache/disk_cache.cc
similarity index 87%
rename from net/disk_cache/cache_creator.cc
rename to net/disk_cache/disk_cache.cc
index f0146deb..81676eb 100644
--- a/net/disk_cache/cache_creator.cc
+++ b/net/disk_cache/disk_cache.cc
@@ -88,8 +88,7 @@
       net_log_(net_log) {
 }
 
-CacheCreator::~CacheCreator() {
-}
+CacheCreator::~CacheCreator() {}
 
 int CacheCreator::Run() {
 #if defined(OS_ANDROID)
@@ -101,14 +100,14 @@
       (backend_type_ == net::CACHE_BACKEND_DEFAULT &&
        kSimpleBackendIsDefault)) {
     disk_cache::SimpleBackendImpl* simple_cache =
-        new disk_cache::SimpleBackendImpl(
-            path_, max_bytes_, type_, thread_, net_log_);
+        new disk_cache::SimpleBackendImpl(path_, max_bytes_, type_, thread_,
+                                          net_log_);
     created_cache_.reset(simple_cache);
     return simple_cache->Init(
         base::Bind(&CacheCreator::OnIOComplete, base::Unretained(this)));
   }
 
-  // Avoid references to blockfile functions on Android to reduce binary size.
+// Avoid references to blockfile functions on Android to reduce binary size.
 #if defined(OS_ANDROID)
   return net::ERR_FAILED;
 #else
@@ -176,17 +175,16 @@
     return *backend ? net::OK : net::ERR_FAILED;
   }
   DCHECK(thread.get());
-  CacheCreator* creator = new CacheCreator(path,
-                                           force,
-                                           max_bytes,
-                                           type,
-                                           backend_type,
-                                           kNone,
-                                           thread,
-                                           net_log,
-                                           backend,
-                                           callback);
+  CacheCreator* creator =
+      new CacheCreator(path, force, max_bytes, type, backend_type, kNone,
+                       thread, net_log, backend, callback);
   return creator->Run();
 }
 
+int Backend::CalculateSizeOfEntriesBetween(base::Time initial_time,
+                                           base::Time end_time,
+                                           const CompletionCallback& callback) {
+  return net::ERR_NOT_IMPLEMENTED;
+}
+
 }  // namespace disk_cache
diff --git a/net/disk_cache/disk_cache.h b/net/disk_cache/disk_cache.h
index f71bc819..0992406 100644
--- a/net/disk_cache/disk_cache.h
+++ b/net/disk_cache/disk_cache.h
@@ -19,6 +19,7 @@
 #include "base/time/time.h"
 #include "net/base/cache_type.h"
 #include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
 #include "net/base/net_export.h"
 
 namespace base {
@@ -149,8 +150,19 @@
   // Calculate the total size of the cache. The return value is the size in
   // bytes or a net error code. If this method returns ERR_IO_PENDING,
   // the |callback| will be invoked when the operation completes.
-  virtual int CalculateSizeOfAllEntries(
-      const CompletionCallback& callback) = 0;
+  virtual int CalculateSizeOfAllEntries(const CompletionCallback& callback) = 0;
+
+  // Calculate the size of all cache entries accessed between |initial_time| and
+  // |end_time|.
+  // The return value is the size in bytes or a net error code. The default
+  // implementation returns ERR_NOT_IMPLEMENTED and should only be overwritten
+  // if there is an efficient way for the backend to determine the size for a
+  // subset of the cache without reading the whole cache from disk.
+  // If this method returns ERR_IO_PENDING, the |callback| will be invoked when
+  // the operation completes.
+  virtual int CalculateSizeOfEntriesBetween(base::Time initial_time,
+                                            base::Time end_time,
+                                            const CompletionCallback& callback);
 
   // Returns an iterator which will enumerate all entries of the cache in an
   // undefined order.
diff --git a/net/disk_cache/disk_cache_test_base.cc b/net/disk_cache/disk_cache_test_base.cc
index 84ba9c6..030c7bd 100644
--- a/net/disk_cache/disk_cache_test_base.cc
+++ b/net/disk_cache/disk_cache_test_base.cc
@@ -179,6 +179,15 @@
   return cb.GetResult(rv);
 }
 
+int DiskCacheTestWithCache::CalculateSizeOfEntriesBetween(
+    const base::Time initial_time,
+    const base::Time end_time) {
+  net::TestCompletionCallback cb;
+  int rv = cache_->CalculateSizeOfEntriesBetween(initial_time, end_time,
+                                                 cb.callback());
+  return cb.GetResult(rv);
+}
+
 std::unique_ptr<DiskCacheTestWithCache::TestIterator>
 DiskCacheTestWithCache::CreateIterator() {
   return std::unique_ptr<TestIterator>(
diff --git a/net/disk_cache/disk_cache_test_base.h b/net/disk_cache/disk_cache_test_base.h
index 9dde5bf..b9564f93 100644
--- a/net/disk_cache/disk_cache_test_base.h
+++ b/net/disk_cache/disk_cache_test_base.h
@@ -138,6 +138,8 @@
   int DoomEntriesBetween(const base::Time initial_time,
                          const base::Time end_time);
   int CalculateSizeOfAllEntries();
+  int CalculateSizeOfEntriesBetween(const base::Time initial_time,
+                                    const base::Time end_time);
   int DoomEntriesSince(const base::Time initial_time);
   std::unique_ptr<TestIterator> CreateIterator();
   void FlushQueueForTest();
diff --git a/net/disk_cache/memory/mem_backend_impl.cc b/net/disk_cache/memory/mem_backend_impl.cc
index ad9dfed..7ded277 100644
--- a/net/disk_cache/memory/mem_backend_impl.cc
+++ b/net/disk_cache/memory/mem_backend_impl.cc
@@ -181,7 +181,6 @@
                                        const CompletionCallback& callback) {
   if (end_time.is_null())
     end_time = Time::Max();
-
   DCHECK_GE(end_time, initial_time);
 
   base::LinkNode<MemEntryImpl>* node = lru_list_.head();
@@ -206,6 +205,26 @@
   return current_size_;
 }
 
+int MemBackendImpl::CalculateSizeOfEntriesBetween(
+    base::Time initial_time,
+    base::Time end_time,
+    const CompletionCallback& callback) {
+  if (end_time.is_null())
+    end_time = Time::Max();
+  DCHECK_GE(end_time, initial_time);
+
+  int size = 0;
+  base::LinkNode<MemEntryImpl>* node = lru_list_.head();
+  while (node != lru_list_.end() && node->value()->GetLastUsed() < initial_time)
+    node = node->next();
+  while (node != lru_list_.end() && node->value()->GetLastUsed() < end_time) {
+    MemEntryImpl* entry = node->value();
+    size += entry->GetStorageSize();
+    node = node->next();
+  }
+  return size;
+}
+
 class MemBackendImpl::MemIterator final : public Backend::Iterator {
  public:
   explicit MemIterator(base::WeakPtr<MemBackendImpl> backend)
diff --git a/net/disk_cache/memory/mem_backend_impl.h b/net/disk_cache/memory/mem_backend_impl.h
index 30417c6..024da855 100644
--- a/net/disk_cache/memory/mem_backend_impl.h
+++ b/net/disk_cache/memory/mem_backend_impl.h
@@ -91,6 +91,10 @@
   int DoomEntriesSince(base::Time initial_time,
                        const CompletionCallback& callback) override;
   int CalculateSizeOfAllEntries(const CompletionCallback& callback) override;
+  int CalculateSizeOfEntriesBetween(
+      base::Time initial_time,
+      base::Time end_time,
+      const CompletionCallback& callback) override;
   std::unique_ptr<Iterator> CreateIterator() override;
   void GetStats(base::StringPairs* stats) override {}
   void OnExternalCacheHit(const std::string& key) override;
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc
index 30fade47..e5645e35 100644
--- a/net/disk_cache/simple/simple_backend_impl.cc
+++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -458,6 +458,15 @@
       &SimpleBackendImpl::IndexReadyForSizeCalculation, AsWeakPtr(), callback));
 }
 
+int SimpleBackendImpl::CalculateSizeOfEntriesBetween(
+    base::Time initial_time,
+    base::Time end_time,
+    const CompletionCallback& callback) {
+  return index_->ExecuteWhenReady(
+      base::Bind(&SimpleBackendImpl::IndexReadyForSizeBetweenCalculation,
+                 AsWeakPtr(), initial_time, end_time, callback));
+}
+
 class SimpleBackendImpl::SimpleIterator final : public Iterator {
  public:
   explicit SimpleIterator(base::WeakPtr<SimpleBackendImpl> backend)
@@ -573,6 +582,18 @@
   callback.Run(result);
 }
 
+void SimpleBackendImpl::IndexReadyForSizeBetweenCalculation(
+    base::Time initial_time,
+    base::Time end_time,
+    const CompletionCallback& callback,
+    int result) {
+  if (result == net::OK) {
+    result =
+        static_cast<int>(index_->GetCacheSizeBetween(initial_time, end_time));
+  }
+  callback.Run(result);
+}
+
 // static
 SimpleBackendImpl::DiskStatResult SimpleBackendImpl::InitCacheStructureOnDisk(
     const base::FilePath& path,
diff --git a/net/disk_cache/simple/simple_backend_impl.h b/net/disk_cache/simple/simple_backend_impl.h
index 3516847..a0b4b9c4 100644
--- a/net/disk_cache/simple/simple_backend_impl.h
+++ b/net/disk_cache/simple/simple_backend_impl.h
@@ -110,6 +110,10 @@
   int DoomEntriesSince(base::Time initial_time,
                        const CompletionCallback& callback) override;
   int CalculateSizeOfAllEntries(const CompletionCallback& callback) override;
+  int CalculateSizeOfEntriesBetween(
+      base::Time initial_time,
+      base::Time end_time,
+      const CompletionCallback& callback) override;
   std::unique_ptr<Iterator> CreateIterator() override;
   void GetStats(base::StringPairs* stats) override;
   void OnExternalCacheHit(const std::string& key) override;
@@ -148,6 +152,13 @@
   void IndexReadyForSizeCalculation(const CompletionCallback& callback,
                                     int result);
 
+  // Calculates the size all cache entries between |initial_time| and
+  // |end_time|. Invoked when the index is ready.
+  void IndexReadyForSizeBetweenCalculation(base::Time initial_time,
+                                           base::Time end_time,
+                                           const CompletionCallback& callback,
+                                           int result);
+
   // Try to create the directory if it doesn't exist. This must run on the IO
   // thread.
   static DiskStatResult InitCacheStructureOnDisk(
diff --git a/net/disk_cache/simple/simple_index.cc b/net/disk_cache/simple/simple_index.cc
index e68e746..a654faf37 100644
--- a/net/disk_cache/simple/simple_index.cc
+++ b/net/disk_cache/simple/simple_index.cc
@@ -220,16 +220,14 @@
     end_time = base::Time::Max();
   else
     end_time += EntryMetadata::GetUpperEpsilonForTimeComparisons();
-  const base::Time extended_end_time =
-      end_time.is_null() ? base::Time::Max() : end_time;
-  DCHECK(extended_end_time >= initial_time);
+  DCHECK(end_time >= initial_time);
+
   std::unique_ptr<HashList> ret_hashes(new HashList());
-  for (EntrySet::iterator it = entries_set_.begin(), end = entries_set_.end();
-       it != end; ++it) {
-    EntryMetadata& metadata = it->second;
+  for (const auto& entry : entries_set_) {
+    const EntryMetadata& metadata = entry.second;
     base::Time entry_time = metadata.GetLastUsedTime();
-    if (initial_time <= entry_time && entry_time < extended_end_time)
-      ret_hashes->push_back(it->first);
+    if (initial_time <= entry_time && entry_time < end_time)
+      ret_hashes->push_back(entry.first);
   }
   return ret_hashes;
 }
@@ -248,6 +246,28 @@
   return cache_size_;
 }
 
+uint64_t SimpleIndex::GetCacheSizeBetween(base::Time initial_time,
+                                          base::Time end_time) const {
+  DCHECK_EQ(true, initialized_);
+
+  if (!initial_time.is_null())
+    initial_time -= EntryMetadata::GetLowerEpsilonForTimeComparisons();
+  if (end_time.is_null())
+    end_time = base::Time::Max();
+  else
+    end_time += EntryMetadata::GetUpperEpsilonForTimeComparisons();
+
+  DCHECK(end_time >= initial_time);
+  uint64_t size = 0;
+  for (const auto& entry : entries_set_) {
+    const EntryMetadata& metadata = entry.second;
+    base::Time entry_time = metadata.GetLastUsedTime();
+    if (initial_time <= entry_time && entry_time < end_time)
+      size += metadata.GetEntrySize();
+  }
+  return size;
+}
+
 void SimpleIndex::Insert(uint64_t entry_hash) {
   DCHECK(io_thread_checker_.CalledOnValidThread());
   // Upon insert we don't know yet the size of the entry.
diff --git a/net/disk_cache/simple/simple_index.h b/net/disk_cache/simple/simple_index.h
index fee2a1e9..1adb0ecc 100644
--- a/net/disk_cache/simple/simple_index.h
+++ b/net/disk_cache/simple/simple_index.h
@@ -155,6 +155,12 @@
   // index has been initialized.
   uint64_t GetCacheSize() const;
 
+  // Returns the size of the cache entries accessed between |initial_time| and
+  // |end_time| in bytes. Can only be called after the index has been
+  // initialized.
+  uint64_t GetCacheSizeBetween(const base::Time initial_time,
+                               const base::Time end_time) const;
+
   // Returns whether the index has been initialized yet.
   bool initialized() const { return initialized_; }
 
diff --git a/net/net.gypi b/net/net.gypi
index 34d13133..1dda9716 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -545,11 +545,11 @@
       'disk_cache/blockfile/trace.h',
       'disk_cache/blockfile/webfonts_histogram.cc',
       'disk_cache/blockfile/webfonts_histogram.h',
-      'disk_cache/cache_creator.cc',
       'disk_cache/cache_util.cc',
       'disk_cache/cache_util.h',
       'disk_cache/cache_util_posix.cc',
       'disk_cache/cache_util_win.cc',
+      'disk_cache/disk_cache.cc',
       'disk_cache/disk_cache.h',
       'disk_cache/memory/mem_backend_impl.cc',
       'disk_cache/memory/mem_backend_impl.h',
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index e6973754..2499915 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1215,8 +1215,6 @@
 crbug.com/619539 [ Win ] http/tests/workers/terminate-during-sync-operation-file.html [ Pass Timeout ]
 crbug.com/619539 [ Win ] virtual/mojo-loading/http/tests/workers/terminate-during-sync-operation-file.html [ Pass Timeout ]
 
-crbug.com/649159 imported/wpt/shadow-dom/slotchange-event.html [ Failure ]
-
 # These are the failing tests because Chrome hasn't implemented according to the spec.
 crbug.com/645988 imported/wpt/uievents/order-of-events/focus-events/focus-manual.html [ Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/accessibility/misspellings.html b/third_party/WebKit/LayoutTests/accessibility/misspellings.html
index 14618b4f..3c75643b 100644
--- a/third_party/WebKit/LayoutTests/accessibility/misspellings.html
+++ b/third_party/WebKit/LayoutTests/accessibility/misspellings.html
@@ -13,100 +13,124 @@
 <textarea id="textarea" spellcheck="true"></textarea>
 
 <script>
-    if (window.internals)
-        internals.setSpellCheckingEnabled(true);
+  if (window.internals)
+    internals.setSpellCheckingEnabled(true);
+  if (window.testRunner)
+    testRunner.setMockSpellCheckerEnabled(true);
+
+  const tests = [];
+
+  tests.push({
+    testObject: async_test('Misspellings should be reported in content editables.'),
+    action: () => {
+      document.getElementById('editable').focus();
+      document.execCommand('InsertText', false, 'Foo baz chello there.');
+      assert_equals(document.getElementById('editable').childNodes.length, 1);
+    },
+    verification: () => {
+      var axEditable = accessibilityController.accessibleElementById('editable');
+      var axStaticText = axEditable.childAtIndex(0);
+
+      assert_equals(axStaticText.misspellingsCount, 3);
+      assert_equals(axStaticText.misspellingAtIndex(0), 'Foo');
+      assert_equals(axStaticText.misspellingAtIndex(1), 'baz');
+      assert_equals(axStaticText.misspellingAtIndex(2), 'chello');
+    },
+    cleanup: () => document.getElementById('editable').style.display = 'none'
+  });
+
+  tests.push({
+    testObject: async_test('Misspellings should be reported in static text when design mode is on.'),
+    action: () => {
+      document.designMode = 'on';
+      // Trigger spell checking on originally static text.
+      window.getSelection().collapse(document.getElementById('paragraph'), 0);
+    },
+    verification: () => {
+      var axParagraph = accessibilityController.accessibleElementById('paragraph');
+      var axStaticText = axParagraph.childAtIndex(0);
+
+      assert_equals(axStaticText.misspellingsCount, 2);
+      assert_equals(axStaticText.misspellingAtIndex(0), 'adaasj');
+      assert_equals(axStaticText.misspellingAtIndex(1), 'sdklj');
+    },
+    cleanup: () => {
+      document.designMode = 'off';
+      document.getElementById('paragraph').style.display = 'none';
+    }
+  });
+
+  tests.push({
+    testObject: async_test('Misspellings should be reported in single-line text fields.'),
+    action: () => {
+      document.getElementById('input').focus();
+      document.execCommand('InsertText', false, 'contentEditable ');
+    },
+    verification: () => {
+      var axInput = accessibilityController.accessibleElementById('input');
+      // If input's shadow DOM changes, this logic might need to be modified.
+      assert_equals(axInput.childrenCount, 1);
+      var axDiv = axInput.childAtIndex(0);
+      assert_equals(axDiv.childrenCount, 1);
+      var axStaticText = axDiv.childAtIndex(0);
+
+      assert_equals(axStaticText.misspellingsCount, 1);
+      assert_equals(axStaticText.misspellingAtIndex(0), 'contentEditable');
+    },
+    cleanup: () => document.getElementById('input').style.display = 'none'
+  });
+
+  tests.push({
+    testObject: async_test('Misspellings should be reported in textareas.'),
+    action: () => {
+      document.getElementById('textarea').focus();
+      document.execCommand('InsertText', false, 'contentEditable ');
+    },
+    verification: () => {
+      var axTextarea = accessibilityController.accessibleElementById('textarea');
+      // If textarea's shadow DOM changes, this logic might need to be modified.
+      assert_equals(axTextarea.childrenCount, 1);
+      var axDiv = axTextarea.childAtIndex(0);
+      assert_equals(axDiv.childrenCount, 1);
+      var axStaticText = axDiv.childAtIndex(0);
+
+      assert_equals(axStaticText.misspellingsCount, 1);
+      assert_equals(axStaticText.misspellingAtIndex(0), 'contentEditable');
+    },
+    cleanup: () => document.getElementById('textarea').style.display = 'none'
+  });
+
+  function runTestIfAny() {
+    const currentTest = tests.shift();
+    if (!currentTest)
+      return;
+
+    const testObject = currentTest.testObject;
+    if (window.testRunner) {
+      testRunner.setSpellCheckResolvedCallback(() => {
+        testObject.step(() => {
+          currentTest.verification();
+          testObject.done();
+        });
+      });
+    }
+    testObject.cleanup = currentTest.cleanup;
+
+    currentTest.action();
+
+    // Force idle time spell checker to run.
     if (window.testRunner)
-        testRunner.setMockSpellCheckerEnabled(true);
+      testRunner.runIdleTasks(() => {});
+  }
 
-    async_test(function(t)
-    {
-        document.getElementById('editable').focus();
-        document.execCommand('InsertText', false, 'Foo baz chello there.');
-        assert_equals(document.getElementById('editable').childNodes.length, 1);
+  add_result_callback(testObject => {
+    setTimeout(() => {
+      if (window.testRunner)
+        testRunner.removeSpellCheckResolvedCallback();
+      testObject.cleanup();
+      runTestIfAny();
+    }, 0);
+  });
 
-        step_timeout(function()
-        {
-            var axEditable = accessibilityController.accessibleElementById('editable');
-            var axStaticText = axEditable.childAtIndex(0);
-
-            assert_equals(axStaticText.misspellingsCount, 3);
-            assert_equals(axStaticText.misspellingAtIndex(0), 'Foo');
-            assert_equals(axStaticText.misspellingAtIndex(1), 'baz');
-            assert_equals(axStaticText.misspellingAtIndex(2), 'chello');
-
-            document.getElementById('editable').style.display = "none";;
-            t.done();
-        }, 50);
-    }, 'Misspellings should be reported in content editables.');
-
-    async_test(function(t)
-    {
-        step_timeout(function() {
-            document.designMode = 'on';
-            // Trigger spell checking on originally static text.
-            window.getSelection().collapse(document.getElementById('paragraph'), 0);
-
-            step_timeout(function()
-            {
-                var axParagraph = accessibilityController.accessibleElementById('paragraph');
-                var axStaticText = axParagraph.childAtIndex(0);
-
-                assert_equals(axStaticText.misspellingsCount, 2);
-                assert_equals(axStaticText.misspellingAtIndex(0), 'adaasj');
-                assert_equals(axStaticText.misspellingAtIndex(1), 'sdklj');
-
-                document.designMode = 'off';
-                document.getElementById('paragraph').style.display = "none";;
-                t.done();
-            }, 50);
-        }, 100);
-    }, 'Misspellings should be reported in static text when design mode is on.');
-
-    async_test(function(t)
-    {
-        step_timeout(function() {
-            document.getElementById('input').focus();
-            document.execCommand('InsertText', false, 'contentEditable ');
-
-            step_timeout(function()
-            {
-                var axInput = accessibilityController.accessibleElementById('input');
-                // If input's shadow DOM changes, this logic might need to be modified.
-                assert_equals(axInput.childrenCount, 1);
-                var axDiv = axInput.childAtIndex(0);
-                assert_equals(axDiv.childrenCount, 1);
-                var axStaticText = axDiv.childAtIndex(0);
-
-                assert_equals(axStaticText.misspellingsCount, 1);
-                assert_equals(axStaticText.misspellingAtIndex(0), 'contentEditable');
-
-                document.getElementById('input').style.display = "none";;
-                t.done();
-            }, 50);
-        }, 200);
-    }, 'Misspellings should be reported in single-line text fields.');
-
-    async_test(function(t)
-    {
-        step_timeout(function() {
-            document.getElementById('textarea').focus();
-            document.execCommand('InsertText', false, 'contentEditable ');
-
-            step_timeout(function()
-            {
-                var axTextarea = accessibilityController.accessibleElementById('textarea');
-                // If textarea's shadow DOM changes, this logic might need to be modified.
-                assert_equals(axTextarea.childrenCount, 1);
-                var axDiv = axTextarea.childAtIndex(0);
-                assert_equals(axDiv.childrenCount, 1);
-                var axStaticText = axDiv.childAtIndex(0);
-
-                assert_equals(axStaticText.misspellingsCount, 1);
-                assert_equals(axStaticText.misspellingAtIndex(0), 'contentEditable');
-
-                document.getElementById('textarea').style.display = "none";;
-                t.done();
-            }, 50);
-        }, 300);
-    }, 'Misspellings should be reported in textareas.');
+  runTestIfAny();
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/background-image-url-setproperty.html b/third_party/WebKit/LayoutTests/fast/css/background-image-url-setproperty.html
new file mode 100644
index 0000000..d3b5790
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/background-image-url-setproperty.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+    var doc = document.implementation.createHTMLDocument("");
+    doc.head.innerHTML = "<style>span { background: url(fail) }</style>";
+    var rule = doc.head.firstChild.sheet.cssRules[0];
+
+    test(() => {
+        assert_equals(rule.style.background, 'url("fail")');
+    }, "Check initial background url.");
+
+    test(() => {
+        rule.style.setProperty("background", "url(pass)");
+        assert_equals(rule.style.background, 'url("pass")');
+    }, "Setting background to a relative url should change its value.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-rect.html b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-rect.html
index 611e2b7..78e9871a 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-rect.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-rect.html
@@ -57,6 +57,35 @@
 }, 'DOMRect setter');
 
 test(() => {
+  var rect = DOMRect.fromRect({x: 10, y: 20, width: 80, height: 50});
+  assert_dom_rect_equals(rect, [10, 20, 80, 50, 20, 90, 70, 10]);
+}, 'DOMRect.fromRect({x: 10, y: 20, width: 80, height: 50}) should create a DOMRect');
+
+test(() => {
+  var rect = DOMRect.fromRect();
+  assert_dom_rect_equals(rect, [0, 0, 0, 0, 0, 0, 0, 0]);
+}, 'DOMRect.fromRect({x: 0, y: 0, width: 0, height: 0}) should create a DOMRect');
+
+test(() => {
+  var rect = DOMRect.fromRect({x: 10, y: 20, width: 80, height: 50});
+  rect.x = 30;
+  assert_dom_rect_equals(rect, [30, 20, 80, 50, 20, 110, 70, 30]);
+  rect.y = -10;
+  assert_dom_rect_equals(rect, [30, -10, 80, 50, -10, 110, 40, 30]);
+  rect.width = 20;
+  assert_dom_rect_equals(rect, [30, -10, 20, 50, -10, 50, 40, 30]);
+  rect.height = 40;
+  assert_dom_rect_equals(rect, [30, -10, 20, 40, -10, 50, 30, 30]);
+}, 'DOMRect.fromRect({x: 10, y: 20, width: 80, height: 50}) should create a DOMRect and the values can be changed');
+
+test(() => {
+  var rect1 = DOMRect.fromRect();
+  var rect2 = DOMRect.fromRect();
+  assert_false(rect1 == rect2);
+  assert_dom_rect_equals(rect1, rect2);
+}, 'DOMRect.fromRect() should create a new DOMRect');
+
+test(() => {
   var rect = new DOMRectReadOnly();
   assert_dom_rect_equals(rect, [0, 0, 0, 0, 0, 0, 0, 0]);
 }, 'DOMRectReadOnly constructor without parameter');
@@ -94,4 +123,29 @@
   assert_object_equals(rect.toJSON(), {x: 10, y: 20, width: 80, height: 50, top: 20, right: 90, bottom: 70, left: 10});
 }, 'DOMRectReadOnly toJSON');
 
+test(() => {
+  var rect = DOMRectReadOnly.fromRect({x: 10, y: 20, width: 80, height: 50});
+  assert_dom_rect_equals(rect, [10, 20, 80, 50, 20, 90, 70, 10]);
+}, 'DOMRectReadOnly.fromRect({x: 10, y: 20, width: 80, height: 50}) should create a DOMRectReadOnly');
+
+test(() => {
+  var rect = DOMRectReadOnly.fromRect();
+  assert_dom_rect_equals(rect, [0, 0, 0, 0, 0, 0, 0, 0]);
+}, 'DOMRectReadOnly.fromRect({x: 0, y: 0, width: 0, height: 0}) should create a DOMRectReadOnly');
+
+test(() => {
+  var rect1 = DOMRectReadOnly.fromRect();
+  var rect2 = DOMRectReadOnly.fromRect();
+  assert_false(rect1 == rect2);
+  assert_dom_rect_equals(rect1, rect2);
+}, 'DOMRectReadOnly.fromRect() should create a new DOMRectReadOnly');
+
+test(() => {
+  var rect = DOMRectReadOnly.fromRect();
+  assert_readonly(rect, 'x');
+  assert_readonly(rect, 'y');
+  assert_readonly(rect, 'width');
+  assert_readonly(rect, 'height');
+}, "DOMRectReadOnly.fromRect() should create DOMRectReadOnly");
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js b/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
index 9e1f966..60ceac0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
+++ b/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
@@ -5,7 +5,7 @@
 "use strict";
 
 const TEST_BUDGET_COST = 1.2;
-const TEST_BUDGET_AT = 2.3;
+const TEST_BUDGET_AT = 2;
 const TEST_BUDGET_TIME = new Date().getTime();
 
 let budgetServiceMock = loadMojoModules(
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 363b2f07..6260f407 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -1160,6 +1160,7 @@
     setter x
     setter y
 interface DOMRectReadOnly
+    static method fromRect
     attribute @@toStringTag
     getter bottom
     getter height
diff --git a/third_party/WebKit/Source/bindings/core/v8/BUILD.gn b/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
index 1a45ad61..d3b5986 100644
--- a/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
+++ b/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
@@ -33,6 +33,8 @@
   "$blink_core_output_dir/dom/DOMPointInit.h",
   "$blink_core_output_dir/dom/DOMQuadInit.cpp",
   "$blink_core_output_dir/dom/DOMQuadInit.h",
+  "$blink_core_output_dir/dom/DOMRectInit.cpp",
+  "$blink_core_output_dir/dom/DOMRectInit.h",
   "$blink_core_output_dir/dom/ElementCreationOptions.cpp",
   "$blink_core_output_dir/dom/ElementCreationOptions.h",
   "$blink_core_output_dir/dom/ElementDefinitionOptions.cpp",
diff --git a/third_party/WebKit/Source/core/core_idl_files.gni b/third_party/WebKit/Source/core/core_idl_files.gni
index d4cd26be..e2ee147 100644
--- a/third_party/WebKit/Source/core/core_idl_files.gni
+++ b/third_party/WebKit/Source/core/core_idl_files.gni
@@ -514,6 +514,7 @@
                     "dom/DOMMatrixInit.idl",
                     "dom/DOMPointInit.idl",
                     "dom/DOMQuadInit.idl",
+                    "dom/DOMRectInit.idl",
                     "dom/ElementCreationOptions.idl",
                     "dom/ElementDefinitionOptions.idl",
                     "dom/ElementRegistrationOptions.idl",
diff --git a/third_party/WebKit/Source/core/css/CSSImageValue.cpp b/third_party/WebKit/Source/core/css/CSSImageValue.cpp
index 2c338c8..c119d1b6 100644
--- a/third_party/WebKit/Source/core/css/CSSImageValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSImageValue.cpp
@@ -102,6 +102,8 @@
 }
 
 bool CSSImageValue::equals(const CSSImageValue& other) const {
+  if (m_absoluteURL.isEmpty() && other.m_absoluteURL.isEmpty())
+    return m_relativeURL == other.m_relativeURL;
   return m_absoluteURL == other.m_absoluteURL;
 }
 
diff --git a/third_party/WebKit/Source/core/dom/DOMRect.cpp b/third_party/WebKit/Source/core/dom/DOMRect.cpp
index 26c811ae..002018a 100644
--- a/third_party/WebKit/Source/core/dom/DOMRect.cpp
+++ b/third_party/WebKit/Source/core/dom/DOMRect.cpp
@@ -4,12 +4,18 @@
 
 #include "core/dom/DOMRect.h"
 
+#include "core/dom/DOMRectInit.h"
+
 namespace blink {
 
 DOMRect* DOMRect::create(double x, double y, double width, double height) {
   return new DOMRect(x, y, width, height);
 }
 
+DOMRect* DOMRect::fromRect(const DOMRectInit& other) {
+  return new DOMRect(other.x(), other.y(), other.width(), other.height());
+}
+
 DOMRect::DOMRect(double x, double y, double width, double height)
     : DOMRectReadOnly(x, y, width, height) {}
 
diff --git a/third_party/WebKit/Source/core/dom/DOMRect.h b/third_party/WebKit/Source/core/dom/DOMRect.h
index 89746a79..b3d66dd0 100644
--- a/third_party/WebKit/Source/core/dom/DOMRect.h
+++ b/third_party/WebKit/Source/core/dom/DOMRect.h
@@ -11,6 +11,9 @@
 
 namespace blink {
 
+class DOMRect;
+class DOMRectInit;
+
 class CORE_EXPORT DOMRect final : public DOMRectReadOnly {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -19,6 +22,7 @@
                          double y = 0,
                          double width = 0,
                          double height = 0);
+  static DOMRect* fromRect(const DOMRectInit&);
 
   void setX(double x) { m_x = x; }
   void setY(double y) { m_y = y; }
diff --git a/third_party/WebKit/Source/core/dom/DOMRect.idl b/third_party/WebKit/Source/core/dom/DOMRect.idl
index 293066b8..782cc41 100644
--- a/third_party/WebKit/Source/core/dom/DOMRect.idl
+++ b/third_party/WebKit/Source/core/dom/DOMRect.idl
@@ -12,6 +12,8 @@
     // FIXME: Exposed=(Window,Worker)
     RuntimeEnabled=GeometryInterfaces,
 ] interface DOMRect : DOMRectReadOnly {
+    [NewObject] static DOMRect fromRect(optional DOMRectInit other);
+
     inherit attribute unrestricted double x;
     inherit attribute unrestricted double y;
     inherit attribute unrestricted double width;
diff --git a/third_party/WebKit/Source/core/dom/DOMRectInit.idl b/third_party/WebKit/Source/core/dom/DOMRectInit.idl
new file mode 100644
index 0000000..2bcc9f3
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/DOMRectInit.idl
@@ -0,0 +1,12 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://drafts.fxtf.org/geometry/#dictdef-domrectinit
+
+dictionary DOMRectInit {
+    unrestricted double x = 0;
+    unrestricted double y = 0;
+    unrestricted double width = 0;
+    unrestricted double height = 0;
+};
diff --git a/third_party/WebKit/Source/core/dom/DOMRectReadOnly.cpp b/third_party/WebKit/Source/core/dom/DOMRectReadOnly.cpp
index 3b09920..6b6e515 100644
--- a/third_party/WebKit/Source/core/dom/DOMRectReadOnly.cpp
+++ b/third_party/WebKit/Source/core/dom/DOMRectReadOnly.cpp
@@ -6,6 +6,7 @@
 
 #include "bindings/core/v8/ScriptValue.h"
 #include "bindings/core/v8/V8ObjectBuilder.h"
+#include "core/dom/DOMRectInit.h"
 
 namespace blink {
 
@@ -29,6 +30,10 @@
   return result.scriptValue();
 }
 
+DOMRectReadOnly* DOMRectReadOnly::fromRect(const DOMRectInit& other) {
+  return new DOMRectReadOnly(other.x(), other.y(), other.width(), other.height());
+}
+
 DOMRectReadOnly::DOMRectReadOnly(double x,
                                  double y,
                                  double width,
diff --git a/third_party/WebKit/Source/core/dom/DOMRectReadOnly.h b/third_party/WebKit/Source/core/dom/DOMRectReadOnly.h
index 1a14e254..957dfc9 100644
--- a/third_party/WebKit/Source/core/dom/DOMRectReadOnly.h
+++ b/third_party/WebKit/Source/core/dom/DOMRectReadOnly.h
@@ -11,6 +11,7 @@
 
 namespace blink {
 
+class DOMRectInit;
 class ScriptValue;
 class ScriptState;
 
@@ -23,6 +24,7 @@
                                  double y,
                                  double width,
                                  double height);
+  static DOMRectReadOnly* fromRect(const DOMRectInit&);
 
   double x() const { return m_x; }
   double y() const { return m_y; }
diff --git a/third_party/WebKit/Source/core/dom/DOMRectReadOnly.idl b/third_party/WebKit/Source/core/dom/DOMRectReadOnly.idl
index f1cecfda..859ecb6 100644
--- a/third_party/WebKit/Source/core/dom/DOMRectReadOnly.idl
+++ b/third_party/WebKit/Source/core/dom/DOMRectReadOnly.idl
@@ -10,6 +10,8 @@
     // FIXME: Exposed=(Window,Worker)
     RuntimeEnabled=GeometryInterfaces,
 ] interface DOMRectReadOnly {
+    [NewObject] static DOMRectReadOnly fromRect(optional DOMRectInit other);
+
     readonly attribute unrestricted double x;
     readonly attribute unrestricted double y;
     readonly attribute unrestricted double width;
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index ed4d6b5..c6fc0601 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -2450,6 +2450,8 @@
   if (registrationContext())
     registrationContext()->documentWasDetached();
 
+  MutationObserver::cleanSlotChangeList(*this);
+
   m_hoverNode = nullptr;
   m_activeHoverElement = nullptr;
   m_autofocusElement = nullptr;
diff --git a/third_party/WebKit/Source/core/dom/MutationObserver.cpp b/third_party/WebKit/Source/core/dom/MutationObserver.cpp
index 9bf8da6..a25ae14 100644
--- a/third_party/WebKit/Source/core/dom/MutationObserver.cpp
+++ b/third_party/WebKit/Source/core/dom/MutationObserver.cpp
@@ -37,6 +37,7 @@
 #include "core/dom/MutationObserverRegistration.h"
 #include "core/dom/MutationRecord.h"
 #include "core/dom/Node.h"
+#include "core/html/HTMLSlotElement.h"
 #include "core/inspector/InspectorInstrumentation.h"
 #include <algorithm>
 
@@ -171,16 +172,45 @@
   return activeObservers;
 }
 
+using SlotChangeList = HeapVector<Member<HTMLSlotElement>>;
+
+// TODO(hayato): We should have a SlotChangeList for each unit of related
+// similar-origin browsing context.
+// https://html.spec.whatwg.org/multipage/browsers.html#unit-of-related-similar-origin-browsing-contexts
+static SlotChangeList& activeSlotChangeList() {
+  DEFINE_STATIC_LOCAL(SlotChangeList, slotChangeList, (new SlotChangeList));
+  return slotChangeList;
+}
+
 static MutationObserverSet& suspendedMutationObservers() {
   DEFINE_STATIC_LOCAL(MutationObserverSet, suspendedObservers,
                       (new MutationObserverSet));
   return suspendedObservers;
 }
 
-static void activateObserver(MutationObserver* observer) {
-  if (activeMutationObservers().isEmpty())
+static void ensureEnqueueMicrotask() {
+  if (activeMutationObservers().isEmpty() && activeSlotChangeList().isEmpty())
     Microtask::enqueueMicrotask(WTF::bind(&MutationObserver::deliverMutations));
+}
 
+void MutationObserver::enqueueSlotChange(HTMLSlotElement& slot) {
+  DCHECK(isMainThread());
+  ensureEnqueueMicrotask();
+  activeSlotChangeList().push_back(&slot);
+}
+
+void MutationObserver::cleanSlotChangeList(Document& document) {
+  SlotChangeList kept;
+  kept.reserveCapacity(activeSlotChangeList().size());
+  for (auto& slot : activeSlotChangeList()) {
+    if (slot->document() != document)
+      kept.push_back(slot);
+  }
+  activeSlotChangeList().swap(kept);
+}
+
+static void activateObserver(MutationObserver* observer) {
+  ensureEnqueueMicrotask();
   activeMutationObservers().add(observer);
 }
 
@@ -257,10 +287,19 @@
 }
 
 void MutationObserver::deliverMutations() {
+  // These steps are defined in DOM Standard's "notify mutation observers".
+  // https://dom.spec.whatwg.org/#notify-mutation-observers
   DCHECK(isMainThread());
+
   MutationObserverVector observers;
   copyToVector(activeMutationObservers(), observers);
   activeMutationObservers().clear();
+
+  SlotChangeList slots;
+  slots.swap(activeSlotChangeList());
+  for (const auto& slot : slots)
+    slot->clearSlotChangeEventEnqueued();
+
   std::sort(observers.begin(), observers.end(), ObserverLessThan());
   for (const auto& observer : observers) {
     if (observer->shouldBeSuspended())
@@ -268,6 +307,8 @@
     else
       observer->deliver();
   }
+  for (const auto& slot : slots)
+    slot->dispatchSlotChangeEvent();
 }
 
 DEFINE_TRACE(MutationObserver) {
diff --git a/third_party/WebKit/Source/core/dom/MutationObserver.h b/third_party/WebKit/Source/core/dom/MutationObserver.h
index 6b35d73..7c7e8c2 100644
--- a/third_party/WebKit/Source/core/dom/MutationObserver.h
+++ b/third_party/WebKit/Source/core/dom/MutationObserver.h
@@ -40,7 +40,9 @@
 
 namespace blink {
 
+class Document;
 class ExceptionState;
+class HTMLSlotElement;
 class MutationCallback;
 class MutationObserver;
 class MutationObserverInit;
@@ -81,6 +83,8 @@
   static MutationObserver* create(MutationCallback*);
   static void resumeSuspendedObservers();
   static void deliverMutations();
+  static void enqueueSlotChange(HTMLSlotElement&);
+  static void cleanSlotChangeList(Document&);
 
   ~MutationObserver();
 
diff --git a/third_party/WebKit/Source/core/fileapi/FileReaderLoader.cpp b/third_party/WebKit/Source/core/fileapi/FileReaderLoader.cpp
index a1e899c..10bec70 100644
--- a/third_party/WebKit/Source/core/fileapi/FileReaderLoader.cpp
+++ b/third_party/WebKit/Source/core/fileapi/FileReaderLoader.cpp
@@ -118,12 +118,14 @@
   CHECK(!m_loader);
   if (m_client) {
     DCHECK(!m_loader);
-    m_loader = ThreadableLoader::create(*executionContext, this, options,
-                                        resourceLoaderOptions);
+    m_loader = ThreadableLoader::create(
+        *executionContext, this, options, resourceLoaderOptions,
+        ThreadableLoader::ClientSpec::kFileReaderLoader);
     m_loader->start(request);
   } else {
     ThreadableLoader::loadResourceSynchronously(
-        *executionContext, request, *this, options, resourceLoaderOptions);
+        *executionContext, request, *this, options, resourceLoaderOptions,
+        ThreadableLoader::ClientSpec::kFileReaderLoader);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp
index 42ce6cf..3f28c62 100644
--- a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp
@@ -149,7 +149,6 @@
 }
 
 void HTMLSlotElement::dispatchSlotChangeEvent() {
-  m_slotchangeEventEnqueued = false;
   Event* event = Event::createBubble(EventTypeNames::slotchange);
   event->setTarget(this);
   dispatchScopedEvent(event);
@@ -331,8 +330,7 @@
 void HTMLSlotElement::enqueueSlotChangeEvent() {
   if (m_slotchangeEventEnqueued)
     return;
-  Microtask::enqueueMicrotask(WTF::bind(
-      &HTMLSlotElement::dispatchSlotChangeEvent, wrapPersistent(this)));
+  MutationObserver::enqueueSlotChange(*this);
   m_slotchangeEventEnqueued = true;
 }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLSlotElement.h b/third_party/WebKit/Source/core/html/HTMLSlotElement.h
index 609f822..babaa81f 100644
--- a/third_party/WebKit/Source/core/html/HTMLSlotElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLSlotElement.h
@@ -93,6 +93,8 @@
 
   bool supportsDistribution() const { return isInV1ShadowTree(); }
   void didSlotChange(SlotChangeType);
+  void dispatchSlotChangeEvent();
+  void clearSlotChangeEventEnqueued() { m_slotchangeEventEnqueued = false; }
 
   static AtomicString normalizeSlotName(const AtomicString&);
 
@@ -106,7 +108,6 @@
   void willRecalcStyle(StyleRecalcChange) final;
 
   void enqueueSlotChangeEvent();
-  void dispatchSlotChangeEvent();
 
   HeapVector<Member<Node>> m_assignedNodes;
   HeapVector<Member<Node>> m_distributedNodes;
diff --git a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
index f40059a..cd542a6 100644
--- a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
@@ -58,6 +58,7 @@
 #include "wtf/Assertions.h"
 #include "wtf/PtrUtil.h"
 #include "wtf/WeakPtr.h"
+#include "wtf/debug/Alias.h"
 #include <memory>
 
 namespace blink {
@@ -119,6 +120,67 @@
   }
 }
 
+// TODO(yhirano): Remove these when https://crbug.com/667254 is fixed.
+void NEVER_INLINE crashWithBlobBytesConsumer() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithEventSource() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithFetchManager() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithFileReaderLoader() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithMainThreadLoaderHolder() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithNotificationImageLoader() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithWebAssociatedURLLoader() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithWorkerScriptLoader() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithXHR() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
+void NEVER_INLINE crashWithTesting() {
+  const char* name = __func__;
+  WTF::debug::alias(&name);
+  CRASH();
+}
+
 }  // namespace
 
 // Max number of CORS redirects handled in DocumentThreadableLoader. Same number
@@ -133,9 +195,10 @@
     const ResourceRequest& request,
     ThreadableLoaderClient& client,
     const ThreadableLoaderOptions& options,
-    const ResourceLoaderOptions& resourceLoaderOptions) {
+    const ResourceLoaderOptions& resourceLoaderOptions,
+    ClientSpec clientSpec) {
   (new DocumentThreadableLoader(document, &client, LoadSynchronously, options,
-                                resourceLoaderOptions))
+                                resourceLoaderOptions, clientSpec))
       ->start(request);
 }
 
@@ -143,9 +206,11 @@
     Document& document,
     ThreadableLoaderClient* client,
     const ThreadableLoaderOptions& options,
-    const ResourceLoaderOptions& resourceLoaderOptions) {
+    const ResourceLoaderOptions& resourceLoaderOptions,
+    ClientSpec clientSpec) {
   return new DocumentThreadableLoader(document, client, LoadAsynchronously,
-                                      options, resourceLoaderOptions);
+                                      options, resourceLoaderOptions,
+                                      clientSpec);
 }
 
 DocumentThreadableLoader::DocumentThreadableLoader(
@@ -153,8 +218,10 @@
     ThreadableLoaderClient* client,
     BlockingBehavior blockingBehavior,
     const ThreadableLoaderOptions& options,
-    const ResourceLoaderOptions& resourceLoaderOptions)
+    const ResourceLoaderOptions& resourceLoaderOptions,
+    ClientSpec clientSpec)
     : m_client(client),
+      m_clientSpec(clientSpec),
       m_document(&document),
       m_options(options),
       m_resourceLoaderOptions(resourceLoaderOptions),
@@ -413,7 +480,42 @@
 }
 
 DocumentThreadableLoader::~DocumentThreadableLoader() {
-  CHECK(!m_client);
+  if (m_client) {
+    auto clientSpec = m_clientSpec;
+    WTF::debug::alias(&clientSpec);
+    switch (m_clientSpec) {
+      case ClientSpec::kBlobBytesConsumer:
+        crashWithBlobBytesConsumer();
+        break;
+      case ClientSpec::kEventSource:
+        crashWithEventSource();
+        break;
+      case ClientSpec::kFetchManager:
+        crashWithFetchManager();
+        break;
+      case ClientSpec::kFileReaderLoader:
+        crashWithFileReaderLoader();
+        break;
+      case ClientSpec::kMainThreadLoaderHolder:
+        crashWithMainThreadLoaderHolder();
+        break;
+      case ClientSpec::kNotificationImageLoader:
+        crashWithNotificationImageLoader();
+        break;
+      case ClientSpec::kWebAssociatedURLLoader:
+        crashWithWebAssociatedURLLoader();
+        break;
+      case ClientSpec::kWorkerScriptLoader:
+        crashWithWorkerScriptLoader();
+        break;
+      case ClientSpec::kXHR:
+        crashWithXHR();
+        break;
+      case ClientSpec::kTesting:
+        crashWithTesting();
+        break;
+    }
+  }
   DCHECK(!m_resource);
 }
 
diff --git a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
index bf1fb5c..0cfdf4a7 100644
--- a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
+++ b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
@@ -62,11 +62,13 @@
                                         const ResourceRequest&,
                                         ThreadableLoaderClient&,
                                         const ThreadableLoaderOptions&,
-                                        const ResourceLoaderOptions&);
+                                        const ResourceLoaderOptions&,
+                                        ThreadableLoader::ClientSpec);
   static DocumentThreadableLoader* create(Document&,
                                           ThreadableLoaderClient*,
                                           const ThreadableLoaderOptions&,
-                                          const ResourceLoaderOptions&);
+                                          const ResourceLoaderOptions&,
+                                          ThreadableLoader::ClientSpec);
   ~DocumentThreadableLoader() override;
 
   void start(const ResourceRequest&) override;
@@ -85,7 +87,8 @@
                            ThreadableLoaderClient*,
                            BlockingBehavior,
                            const ThreadableLoaderOptions&,
-                           const ResourceLoaderOptions&);
+                           const ResourceLoaderOptions&,
+                           ClientSpec);
 
   void clear();
 
@@ -185,6 +188,7 @@
   Document& document() const;
 
   ThreadableLoaderClient* m_client;
+  const ClientSpec m_clientSpec;
   Member<Document> m_document;
 
   const ThreadableLoaderOptions m_options;
diff --git a/third_party/WebKit/Source/core/loader/ThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/ThreadableLoader.cpp
index 11cedc5..1fee35a7 100644
--- a/third_party/WebKit/Source/core/loader/ThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/ThreadableLoader.cpp
@@ -42,7 +42,8 @@
     ExecutionContext& context,
     ThreadableLoaderClient* client,
     const ThreadableLoaderOptions& options,
-    const ResourceLoaderOptions& resourceLoaderOptions) {
+    const ResourceLoaderOptions& resourceLoaderOptions,
+    ClientSpec clientSpec) {
   DCHECK(client);
 
   if (context.isWorkerGlobalScope()) {
@@ -51,7 +52,7 @@
   }
 
   return DocumentThreadableLoader::create(toDocument(context), client, options,
-                                          resourceLoaderOptions);
+                                          resourceLoaderOptions, clientSpec);
 }
 
 void ThreadableLoader::loadResourceSynchronously(
@@ -59,7 +60,8 @@
     const ResourceRequest& request,
     ThreadableLoaderClient& client,
     const ThreadableLoaderOptions& options,
-    const ResourceLoaderOptions& resourceLoaderOptions) {
+    const ResourceLoaderOptions& resourceLoaderOptions,
+    ClientSpec clientSpec) {
   if (context.isWorkerGlobalScope()) {
     WorkerThreadableLoader::loadResourceSynchronously(
         toWorkerGlobalScope(context), request, client, options,
@@ -68,7 +70,8 @@
   }
 
   DocumentThreadableLoader::loadResourceSynchronously(
-      toDocument(context), request, client, options, resourceLoaderOptions);
+      toDocument(context), request, client, options, resourceLoaderOptions,
+      clientSpec);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/ThreadableLoader.h b/third_party/WebKit/Source/core/loader/ThreadableLoader.h
index 9e8c56c..39618fe 100644
--- a/third_party/WebKit/Source/core/loader/ThreadableLoader.h
+++ b/third_party/WebKit/Source/core/loader/ThreadableLoader.h
@@ -131,11 +131,26 @@
   WTF_MAKE_NONCOPYABLE(ThreadableLoader);
 
  public:
+  // TODO(yhirano): Remove this enum once https://crbug.com/667254 is fixed.
+  enum class ClientSpec {
+    kBlobBytesConsumer,
+    kEventSource,
+    kFetchManager,
+    kFileReaderLoader,
+    kMainThreadLoaderHolder,
+    kNotificationImageLoader,
+    kWebAssociatedURLLoader,
+    kWorkerScriptLoader,
+    kXHR,
+    kTesting,
+  };
+
   static void loadResourceSynchronously(ExecutionContext&,
                                         const ResourceRequest&,
                                         ThreadableLoaderClient&,
                                         const ThreadableLoaderOptions&,
-                                        const ResourceLoaderOptions&);
+                                        const ResourceLoaderOptions&,
+                                        ClientSpec);
 
   // This method never returns nullptr.
   //
@@ -171,7 +186,8 @@
   static ThreadableLoader* create(ExecutionContext&,
                                   ThreadableLoaderClient*,
                                   const ThreadableLoaderOptions&,
-                                  const ResourceLoaderOptions&);
+                                  const ResourceLoaderOptions&,
+                                  ClientSpec);
 
   // The methods on the ThreadableLoaderClient passed on create() call
   // may be called synchronous to start() call.
diff --git a/third_party/WebKit/Source/core/loader/ThreadableLoaderTest.cpp b/third_party/WebKit/Source/core/loader/ThreadableLoaderTest.cpp
index de7b728..41ebcca 100644
--- a/third_party/WebKit/Source/core/loader/ThreadableLoaderTest.cpp
+++ b/third_party/WebKit/Source/core/loader/ThreadableLoaderTest.cpp
@@ -128,8 +128,9 @@
     ThreadableLoaderOptions options;
     options.crossOriginRequestPolicy = crossOriginRequestPolicy;
     ResourceLoaderOptions resourceLoaderOptions;
-    m_loader = DocumentThreadableLoader::create(document(), client, options,
-                                                resourceLoaderOptions);
+    m_loader = DocumentThreadableLoader::create(
+        document(), client, options, resourceLoaderOptions,
+        ThreadableLoader::ClientSpec::kTesting);
   }
 
   void startLoader(const ResourceRequest& request) override {
@@ -300,7 +301,8 @@
     DCHECK(m_workerThread->globalScope()->isWorkerGlobalScope());
 
     m_loader = ThreadableLoader::create(*m_workerThread->globalScope(), client,
-                                        options, resourceLoaderOptions);
+                                        options, resourceLoaderOptions,
+                                        ThreadableLoader::ClientSpec::kTesting);
     DCHECK(m_loader);
     event->signal();
   }
diff --git a/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
index f54172d4..f0e49d8 100644
--- a/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
@@ -671,8 +671,9 @@
   resourceLoaderOptions.requestInitiatorContext = WorkerContext;
   // TODO(yhirano): Remove this CHECK once https://crbug.com/667254 is fixed.
   CHECK(!m_mainThreadLoader);
-  m_mainThreadLoader = DocumentThreadableLoader::create(document, this, options,
-                                                        resourceLoaderOptions);
+  m_mainThreadLoader = DocumentThreadableLoader::create(
+      document, this, options, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kMainThreadLoaderHolder);
   m_mainThreadLoader->start(ResourceRequest(request.get()));
 }
 
diff --git a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
index 64d6f9b..5fe8c88 100644
--- a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.cpp
@@ -112,8 +112,9 @@
   m_needToCancel = true;
   // TODO(yhirano): Remove this CHECK once https://crbug.com/667254 is fixed.
   CHECK(!m_threadableLoader);
-  m_threadableLoader = ThreadableLoader::create(executionContext, this, options,
-                                                resourceLoaderOptions);
+  m_threadableLoader = ThreadableLoader::create(
+      executionContext, this, options, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kWorkerScriptLoader);
   m_threadableLoader->start(request);
   if (m_failed)
     notifyFinished();
diff --git a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
index 74da603..438a1786 100644
--- a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
+++ b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
@@ -1046,7 +1046,8 @@
     CHECK(!m_loader);
     DCHECK(m_sendFlag);
     m_loader = ThreadableLoader::create(executionContext, this, options,
-                                        resourceLoaderOptions);
+                                        resourceLoaderOptions,
+                                        ThreadableLoader::ClientSpec::kXHR);
     m_loader->start(request);
 
     return;
@@ -1056,8 +1057,9 @@
   CHECK(!m_loader);
   // Use count for XHR synchronous requests.
   UseCounter::count(&executionContext, UseCounter::XMLHttpRequestSynchronous);
-  ThreadableLoader::loadResourceSynchronously(executionContext, request, *this,
-                                              options, resourceLoaderOptions);
+  ThreadableLoader::loadResourceSynchronously(
+      executionContext, request, *this, options, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kXHR);
 
   throwForLoadFailureIfNeeded(exceptionState, String());
 }
diff --git a/third_party/WebKit/Source/modules/budget/BudgetService.cpp b/third_party/WebKit/Source/modules/budget/BudgetService.cpp
index 5308d86..1def709 100644
--- a/third_party/WebKit/Source/modules/budget/BudgetService.cpp
+++ b/third_party/WebKit/Source/modules/budget/BudgetService.cpp
@@ -114,9 +114,12 @@
 
   // Copy the chunks into the budget array.
   HeapVector<Member<BudgetState>> budget(expectations.size());
-  for (size_t i = 0; i < expectations.size(); i++)
-    budget[i] =
-        new BudgetState(expectations[i]->budget_at, expectations[i]->time);
+  for (size_t i = 0; i < expectations.size(); i++) {
+    // Return the largest integer less than the budget, so it's easier for
+    // developer to reason about budget.
+    budget[i] = new BudgetState(floor(expectations[i]->budget_at),
+                                expectations[i]->time);
+  }
 
   resolver->resolve(budget);
 }
diff --git a/third_party/WebKit/Source/modules/eventsource/EventSource.cpp b/third_party/WebKit/Source/modules/eventsource/EventSource.cpp
index d9eb5be..f8be89b6 100644
--- a/third_party/WebKit/Source/modules/eventsource/EventSource.cpp
+++ b/third_party/WebKit/Source/modules/eventsource/EventSource.cpp
@@ -177,8 +177,9 @@
   CHECK(!m_loader);
   // InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient
   // will be called synchronously.
-  m_loader = ThreadableLoader::create(executionContext, this, options,
-                                      resourceLoaderOptions);
+  m_loader = ThreadableLoader::create(
+      executionContext, this, options, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kEventSource);
   m_loader->start(request);
 }
 
diff --git a/third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp b/third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp
index 1ddf1a9..86b65b0 100644
--- a/third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp
+++ b/third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp
@@ -278,8 +278,9 @@
   ResourceLoaderOptions resourceLoaderOptions;
   resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
 
-  return ThreadableLoader::create(*getExecutionContext(), this, options,
-                                  resourceLoaderOptions);
+  return ThreadableLoader::create(
+      *getExecutionContext(), this, options, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kBlobBytesConsumer);
 }
 
 void BlobBytesConsumer::close() {
diff --git a/third_party/WebKit/Source/modules/fetch/FetchManager.cpp b/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
index fb8396e..a14ed48 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
@@ -840,9 +840,9 @@
   InspectorInstrumentation::willStartFetch(m_executionContext, this);
   // TODO(yhirano): Remove this CHECK once https://crbug.com/667254 is fixed.
   CHECK(!m_loader);
-  m_loader =
-      ThreadableLoader::create(*m_executionContext, this,
-                               threadableLoaderOptions, resourceLoaderOptions);
+  m_loader = ThreadableLoader::create(
+      *m_executionContext, this, threadableLoaderOptions, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kFetchManager);
   m_loader->start(request);
 }
 
@@ -873,9 +873,9 @@
   threadableLoaderOptions.crossOriginRequestPolicy = AllowCrossOriginRequests;
 
   InspectorInstrumentation::willStartFetch(m_executionContext, this);
-  m_loader =
-      ThreadableLoader::create(*m_executionContext, this,
-                               threadableLoaderOptions, resourceLoaderOptions);
+  m_loader = ThreadableLoader::create(
+      *m_executionContext, this, threadableLoaderOptions, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kFetchManager);
   m_loader->start(request);
 }
 
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationImageLoader.cpp b/third_party/WebKit/Source/modules/notifications/NotificationImageLoader.cpp
index 9cec819..09f3c76 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationImageLoader.cpp
+++ b/third_party/WebKit/Source/modules/notifications/NotificationImageLoader.cpp
@@ -123,7 +123,8 @@
   // TODO(yhirano): Remove this CHECK once https://crbug.com/667254 is fixed.
   CHECK(!m_threadableLoader);
   m_threadableLoader = ThreadableLoader::create(
-      *executionContext, this, threadableLoaderOptions, resourceLoaderOptions);
+      *executionContext, this, threadableLoaderOptions, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kNotificationImageLoader);
   m_threadableLoader->start(resourceRequest);
 }
 
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index b567a242..41a435f 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1208,6 +1208,8 @@
     "scheduler/base/work_queue_sets.cc",
     "scheduler/base/work_queue_sets.h",
     "scheduler/child/compositor_worker_scheduler.cc",
+    "scheduler/child/idle_canceled_delayed_task_sweeper.cc",
+    "scheduler/child/idle_canceled_delayed_task_sweeper.h",
     "scheduler/child/idle_helper.cc",
     "scheduler/child/idle_helper.h",
     "scheduler/child/scheduler_helper.cc",
@@ -1785,6 +1787,7 @@
     "scheduler/base/time_domain_unittest.cc",
     "scheduler/base/work_queue_sets_unittest.cc",
     "scheduler/base/work_queue_unittest.cc",
+    "scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc",
     "scheduler/child/idle_helper_unittest.cc",
     "scheduler/child/scheduler_helper_unittest.cc",
     "scheduler/child/scheduler_tqm_delegate_impl_unittest.cc",
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
index e3a3b584..a0bc142 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.cc
@@ -814,6 +814,28 @@
   return base::MakeUnique<QueueEnabledVoterImpl>(this);
 }
 
+void TaskQueueImpl::SweepCanceledDelayedTasks(base::TimeTicks now) {
+  if (main_thread_only().delayed_incoming_queue.empty())
+    return;
+
+  base::TimeTicks first_task_runtime =
+      main_thread_only().delayed_incoming_queue.top().delayed_run_time;
+
+  // TODO(alexclarke): Let this remove all tasks once the DoWork refactor has
+  // landed.
+  std::priority_queue<Task> remaining_tasks;
+  while (!main_thread_only().delayed_incoming_queue.empty()) {
+    if (!main_thread_only().delayed_incoming_queue.top().task.IsCancelled() ||
+        main_thread_only().delayed_incoming_queue.top().delayed_run_time ==
+            first_task_runtime) {
+      remaining_tasks.push(std::move(
+          const_cast<Task&>(main_thread_only().delayed_incoming_queue.top())));
+    }
+    main_thread_only().delayed_incoming_queue.pop();
+  }
+  main_thread_only().delayed_incoming_queue = std::move(remaining_tasks);
+}
+
 }  // namespace internal
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
index f293c5c..6f85115 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_impl.h
@@ -212,6 +212,9 @@
     bool enabled_;
   };
 
+  // Iterates over |delayed_incoming_queue| removing canceled tasks.
+  void SweepCanceledDelayedTasks(base::TimeTicks now);
+
  private:
   friend class WorkQueue;
   friend class WorkQueueTest;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
index 74a311b1..cbfc90c 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
@@ -494,5 +494,15 @@
   return !selector_.EnabledWorkQueuesEmpty();
 }
 
+void TaskQueueManager::SweepCanceledDelayedTasks() {
+  std::map<TimeDomain*, base::TimeTicks> time_domain_now;
+  for (const scoped_refptr<internal::TaskQueueImpl>& queue : queues_) {
+    TimeDomain* time_domain = queue->GetTimeDomain();
+    if (time_domain_now.find(time_domain) == time_domain_now.end())
+      time_domain_now.insert(std::make_pair(time_domain, time_domain->Now()));
+    queue->SweepCanceledDelayedTasks(time_domain_now[time_domain]);
+  }
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
index 0fbfa7e..53b9560 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
@@ -140,6 +140,9 @@
   // Returns true if there is a task that could be executed immediately.
   bool HasImmediateWorkForTesting() const;
 
+  // Removes all canceled delayed tasks.
+  void SweepCanceledDelayedTasks();
+
  private:
   friend class LazyNow;
   friend class internal::TaskQueueImpl;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
index 97ef5e95..e16b5c6 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
@@ -2162,5 +2162,43 @@
   voter.reset();
 }
 
+TEST_F(TaskQueueManagerTest, SweepCanceledDelayedTasks) {
+  Initialize(1u);
+
+  CancelableTask task1(now_src_.get());
+  CancelableTask task2(now_src_.get());
+  CancelableTask task3(now_src_.get());
+  CancelableTask task4(now_src_.get());
+  base::TimeDelta delay1(base::TimeDelta::FromSeconds(5));
+  base::TimeDelta delay2(base::TimeDelta::FromSeconds(10));
+  base::TimeDelta delay3(base::TimeDelta::FromSeconds(15));
+  base::TimeDelta delay4(base::TimeDelta::FromSeconds(30));
+  std::vector<base::TimeTicks> run_times;
+  runners_[0]->PostDelayedTask(
+      FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask,
+                            task1.weak_factory_.GetWeakPtr(), &run_times),
+      delay1);
+  runners_[0]->PostDelayedTask(
+      FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask,
+                            task2.weak_factory_.GetWeakPtr(), &run_times),
+      delay2);
+  runners_[0]->PostDelayedTask(
+      FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask,
+                            task3.weak_factory_.GetWeakPtr(), &run_times),
+      delay3);
+  runners_[0]->PostDelayedTask(
+      FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask,
+                            task4.weak_factory_.GetWeakPtr(), &run_times),
+      delay4);
+
+  EXPECT_EQ(4u, runners_[0]->GetNumberOfPendingTasks());
+  task2.weak_factory_.InvalidateWeakPtrs();
+  task3.weak_factory_.InvalidateWeakPtrs();
+  EXPECT_EQ(4u, runners_[0]->GetNumberOfPendingTasks());
+
+  manager_->SweepCanceledDelayedTasks();
+  EXPECT_EQ(2u, runners_[0]->GetNumberOfPendingTasks());
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/child/compositor_worker_scheduler.cc b/third_party/WebKit/Source/platform/scheduler/child/compositor_worker_scheduler.cc
index 4a8d07cf..d064d35b5 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/compositor_worker_scheduler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/compositor_worker_scheduler.cc
@@ -175,5 +175,9 @@
 
 void CompositorWorkerScheduler::DidProcessIdleTask() {}
 
+base::TimeTicks CompositorWorkerScheduler::NowTicks() {
+  return base::TimeTicks::Now();
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper.cc b/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper.cc
new file mode 100644
index 0000000..ed969fb
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
+
+#include "base/bind.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+const int kDelayedTaskSweepIntervalSeconds = 30;
+}
+
+IdleCanceledDelayedTaskSweeper::IdleCanceledDelayedTaskSweeper(
+    const char* tracing_category,
+    SchedulerHelper* scheduler_helper,
+    scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner)
+    : tracing_category_(tracing_category),
+      scheduler_helper_(scheduler_helper),
+      idle_task_runner_(idle_task_runner),
+      weak_factory_(this) {
+  PostIdleTask();
+}
+
+void IdleCanceledDelayedTaskSweeper::PostIdleTask() {
+  idle_task_runner_->PostDelayedIdleTask(
+      FROM_HERE, base::TimeDelta::FromSeconds(kDelayedTaskSweepIntervalSeconds),
+      base::Bind(&IdleCanceledDelayedTaskSweeper::SweepIdleTask,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void IdleCanceledDelayedTaskSweeper::SweepIdleTask(base::TimeTicks deadline) {
+  TRACE_EVENT0(tracing_category_,
+               "IdleCanceledDelayedTaskSweeper::SweepIdleTask");
+  scheduler_helper_->SweepCanceledDelayedTasks();
+  PostIdleTask();
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h b/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h
new file mode 100644
index 0000000..7482b147
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_CHILD_IDLE_CANCELED_DELAYED_TASK_SWEEPER_H_
+#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_CHILD_IDLE_CANCELED_DELAYED_TASK_SWEEPER_H_
+
+#include "base/macros.h"
+#include "platform/scheduler/child/scheduler_helper.h"
+#include "public/platform/scheduler/child/single_thread_idle_task_runner.h"
+
+namespace blink {
+namespace scheduler {
+
+// This class periodically sweeps away canceled delayed tasks, which helps
+// reduce memory consumption.
+class BLINK_PLATFORM_EXPORT IdleCanceledDelayedTaskSweeper {
+ public:
+  IdleCanceledDelayedTaskSweeper(
+      const char* tracing_category,
+      SchedulerHelper* scheduler_helper,
+      scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner);
+
+ private:
+  void PostIdleTask();
+  void SweepIdleTask(base::TimeTicks deadline);
+
+  const char* tracing_category_;       // NOT OWNED
+  SchedulerHelper* scheduler_helper_;  // NOT OWNED
+  scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
+  base::WeakPtrFactory<IdleCanceledDelayedTaskSweeper> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(IdleCanceledDelayedTaskSweeper);
+};
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_CHILD_IDLE_CANCELED_DELAYED_TASK_SWEEPER_H_
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc
new file mode 100644
index 0000000..c9802db3
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "cc/test/ordered_simple_task_runner.h"
+#include "public/platform/scheduler/base/task_queue.h"
+#include "platform/scheduler/base/lazy_now.h"
+#include "platform/scheduler/base/test_time_source.h"
+#include "platform/scheduler/child/idle_helper.h"
+#include "platform/scheduler/child/scheduler_helper.h"
+#include "platform/scheduler/child/scheduler_tqm_delegate_for_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+class TestClass {
+ public:
+  TestClass() : weak_factory_(this) {}
+
+  void NopTask() {}
+
+  base::WeakPtrFactory<TestClass> weak_factory_;
+};
+
+class IdleCanceledDelayedTaskSweeperTest : public testing::Test,
+                                           public IdleHelper::Delegate {
+ public:
+  IdleCanceledDelayedTaskSweeperTest()
+      : clock_(new base::SimpleTestTickClock()),
+        mock_task_runner_(new cc::OrderedSimpleTaskRunner(clock_.get(), true)),
+        delegate_(SchedulerTqmDelegateForTest::Create(
+            mock_task_runner_,
+            base::WrapUnique(new TestTimeSource(clock_.get())))),
+        scheduler_helper_(new SchedulerHelper(
+            delegate_,
+            "test.scheduler",
+            TRACE_DISABLED_BY_DEFAULT("test.scheduler"),
+            TRACE_DISABLED_BY_DEFAULT("test.scheduler.dbg"))),
+        idle_helper_(
+            new IdleHelper(scheduler_helper_.get(),
+                           this,
+                           "test.scheduler",
+                           TRACE_DISABLED_BY_DEFAULT("test.scheduler"),
+                           TRACE_DISABLED_BY_DEFAULT("test.scheduler.dbg"),
+                           base::TimeDelta::FromSeconds(30))),
+        idle_canceled_delayed_taks_sweeper_(
+            new IdleCanceledDelayedTaskSweeper("test",
+                                               scheduler_helper_.get(),
+                                               idle_helper_->IdleTaskRunner())),
+        default_task_runner_(scheduler_helper_->DefaultTaskRunner()) {
+    clock_->Advance(base::TimeDelta::FromMicroseconds(5000));
+  }
+
+  ~IdleCanceledDelayedTaskSweeperTest() override {}
+
+  void TearDown() override {
+    // Check that all tests stop posting tasks.
+    mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+    while (mock_task_runner_->RunUntilIdle()) {
+    }
+  }
+
+  // IdleHelper::Delegate implementation:
+  bool CanEnterLongIdlePeriod(
+      base::TimeTicks now,
+      base::TimeDelta* next_long_idle_period_delay_out) override {
+    return true;
+  }
+  void IsNotQuiescent() override {}
+  void OnIdlePeriodStarted() override {}
+  void OnIdlePeriodEnded() override {}
+
+ protected:
+  std::unique_ptr<base::SimpleTestTickClock> clock_;
+  scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+
+  scoped_refptr<SchedulerTqmDelegateForTest> delegate_;
+  std::unique_ptr<SchedulerHelper> scheduler_helper_;
+  std::unique_ptr<IdleHelper> idle_helper_;
+  std::unique_ptr<IdleCanceledDelayedTaskSweeper>
+      idle_canceled_delayed_taks_sweeper_;
+  scoped_refptr<TaskQueue> default_task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(IdleCanceledDelayedTaskSweeperTest);
+};
+
+TEST_F(IdleCanceledDelayedTaskSweeperTest, TestSweep) {
+  TestClass class1;
+  TestClass class2;
+
+  // Post one task we won't cancel.
+  default_task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&TestClass::NopTask, class1.weak_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(100));
+
+  // And a bunch we will.
+  default_task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&TestClass::NopTask, class2.weak_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(101));
+
+  default_task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&TestClass::NopTask, class2.weak_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(102));
+
+  default_task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&TestClass::NopTask, class2.weak_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(103));
+
+  default_task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&TestClass::NopTask, class2.weak_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(104));
+
+  // Cancel the last four tasks.
+  class2.weak_factory_.InvalidateWeakPtrs();
+
+  // Give the IdleCanceledDelayedTaskSweeper a chance to run but don't let
+  // the first non canceled delayed task run.  This is important because the
+  // canceled tasks would get removed by TaskQueueImpl::WakeUpForDelayedWork.
+  clock_->Advance(base::TimeDelta::FromSeconds(40));
+  idle_helper_->EnableLongIdlePeriod();
+  mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(40));
+
+  EXPECT_EQ(1u, default_task_runner_->GetNumberOfPendingTasks());
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_helper.cc b/third_party/WebKit/Source/platform/scheduler/child/idle_helper.cc
index e093552..07a8e08a 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/idle_helper.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_helper.cc
@@ -174,6 +174,9 @@
   helper_->CheckOnValidThread();
   DCHECK(IsInIdlePeriod(new_state));
 
+  // Allow any ready delayed idle tasks to run.
+  idle_task_runner_->EnqueueReadyDelayedIdleTasks();
+
   base::TimeDelta idle_period_duration(idle_period_deadline - now);
   if (idle_period_duration <
       base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) {
@@ -321,6 +324,10 @@
   }
 }
 
+base::TimeTicks IdleHelper::NowTicks() {
+  return helper_->scheduler_tqm_delegate()->NowTicks();
+}
+
 // static
 bool IdleHelper::IsInIdlePeriod(IdlePeriodState state) {
   return state != IdlePeriodState::NOT_IN_IDLE_PERIOD;
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_helper.h b/third_party/WebKit/Source/platform/scheduler/child/idle_helper.h
index 5f0ccd7..602f1b2e 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/idle_helper.h
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_helper.h
@@ -138,6 +138,7 @@
   void OnIdleTaskPosted() override;
   base::TimeTicks WillProcessIdleTask() override;
   void DidProcessIdleTask() override;
+  base::TimeTicks NowTicks() override;
 
   // base::MessageLoop::TaskObserver implementation:
   void WillProcessTask(const base::PendingTask& pending_task) override;
@@ -150,6 +151,8 @@
   friend class BaseIdleHelperTest;
   friend class IdleHelperTest;
 
+  const scoped_refptr<TaskQueue>& idle_queue() const { return idle_queue_; }
+
   class State {
    public:
     State(SchedulerHelper* helper,
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_helper_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/idle_helper_unittest.cc
index f7ea8c15e..9826020 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/idle_helper_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_helper_unittest.cc
@@ -166,7 +166,7 @@
 
   ~IdleHelperForTest() override {}
 
-  // SchedulerHelperDelegate implementation:
+  // IdleHelper::Delegate implementation:
   MOCK_METHOD2(CanEnterLongIdlePeriod,
                bool(base::TimeTicks now,
                     base::TimeDelta* next_long_idle_period_delay_out));
@@ -284,6 +284,10 @@
                                idle_helper_->SchedulerIdlePeriodState()));
   }
 
+  const scoped_refptr<TaskQueue>& idle_queue() const {
+    return idle_helper_->idle_queue_;
+  }
+
   std::unique_ptr<base::SimpleTestTickClock> clock_;
   // Only one of mock_task_runner_ or message_loop_ will be set.
   scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
@@ -1080,5 +1084,39 @@
   EXPECT_EQ(0, run_count);
 }
 
+TEST_F(IdleHelperTest, TestPostDelayedIdleTask) {
+  int run_count = 0;
+  base::TimeTicks expected_deadline =
+      clock_->NowTicks() + base::TimeDelta::FromMilliseconds(2300);
+  base::TimeTicks deadline_in_task;
+
+  // Posting a delayed idle task should not post anything on the underlying
+  // task queue until the delay is up.
+  idle_task_runner_->PostDelayedIdleTask(
+      FROM_HERE, base::TimeDelta::FromMilliseconds(200),
+      base::Bind(&IdleTestTask, &run_count, &deadline_in_task));
+  EXPECT_EQ(0u, idle_queue()->GetNumberOfPendingTasks());
+
+  clock_->Advance(base::TimeDelta::FromMilliseconds(100));
+
+  // It shouldn't run until the delay is over even though we went idle.
+  idle_helper_->StartIdlePeriod(
+      IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->NowTicks(),
+      expected_deadline);
+  EXPECT_EQ(0u, idle_queue()->GetNumberOfPendingTasks());
+  RunUntilIdle();
+  EXPECT_EQ(0, run_count);
+
+  clock_->Advance(base::TimeDelta::FromMilliseconds(100));
+  idle_helper_->StartIdlePeriod(
+      IdleHelper::IdlePeriodState::IN_SHORT_IDLE_PERIOD, clock_->NowTicks(),
+      expected_deadline);
+  EXPECT_EQ(1u, idle_queue()->GetNumberOfPendingTasks());
+  RunUntilIdle();
+
+  EXPECT_EQ(1, run_count);
+  EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
index 5870d2f..f33bffa 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.cc
@@ -129,6 +129,12 @@
   task_queue_manager_->SetObserver(this);
 }
 
+void SchedulerHelper::SweepCanceledDelayedTasks() {
+  CheckOnValidThread();
+  DCHECK(task_queue_manager_);
+  task_queue_manager_->SweepCanceledDelayedTasks();
+}
+
 RealTimeDomain* SchedulerHelper::real_time_domain() const {
   CheckOnValidThread();
   DCHECK(task_queue_manager_);
diff --git a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
index 220ab7a..3a62d523 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
+++ b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper.h
@@ -86,6 +86,9 @@
   // Note |observer| is expected to outlive the SchedulerHelper.
   void SetObserver(Observer* observer);
 
+  // Remove all canceled delayed tasks.
+  void SweepCanceledDelayedTasks();
+
   // Accessor methods.
   RealTimeDomain* real_time_domain() const;
   void RegisterTimeDomain(TimeDomain* time_domain);
diff --git a/third_party/WebKit/Source/platform/scheduler/child/single_thread_idle_task_runner.cc b/third_party/WebKit/Source/platform/scheduler/child/single_thread_idle_task_runner.cc
index b6df36b6..2ffc75b 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/single_thread_idle_task_runner.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/single_thread_idle_task_runner.cc
@@ -44,6 +44,17 @@
                             weak_scheduler_ptr_, idle_task));
 }
 
+void SingleThreadIdleTaskRunner::PostDelayedIdleTask(
+    const tracked_objects::Location& from_here,
+    const base::TimeDelta delay,
+    const IdleTask& idle_task) {
+  base::TimeTicks first_run_time = delegate_->NowTicks() + delay;
+  delayed_idle_tasks_.insert(std::make_pair(
+      first_run_time,
+      std::make_pair(from_here, base::Bind(&SingleThreadIdleTaskRunner::RunTask,
+                                           weak_scheduler_ptr_, idle_task))));
+}
+
 void SingleThreadIdleTaskRunner::PostNonNestableIdleTask(
     const tracked_objects::Location& from_here,
     const IdleTask& idle_task) {
@@ -53,6 +64,20 @@
                             weak_scheduler_ptr_, idle_task));
 }
 
+void SingleThreadIdleTaskRunner::EnqueueReadyDelayedIdleTasks() {
+  if (delayed_idle_tasks_.empty())
+    return;
+
+  base::TimeTicks now = delegate_->NowTicks();
+  while (!delayed_idle_tasks_.empty() &&
+         delayed_idle_tasks_.begin()->first <= now) {
+    idle_priority_task_runner_->PostTask(
+        delayed_idle_tasks_.begin()->second.first,
+        std::move(delayed_idle_tasks_.begin()->second.second));
+    delayed_idle_tasks_.erase(delayed_idle_tasks_.begin());
+  }
+}
+
 void SingleThreadIdleTaskRunner::RunTask(IdleTask idle_task) {
   base::TimeTicks deadline = delegate_->WillProcessIdleTask();
   TRACE_EVENT1(tracing_category_, "SingleThreadIdleTaskRunner::RunTask",
diff --git a/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl.cc
index 636c10fe..192502a3 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl.cc
@@ -24,7 +24,10 @@
                    "worker.scheduler",
                    TRACE_DISABLED_BY_DEFAULT("worker.scheduler"),
                    "WorkerSchedulerIdlePeriod",
-                   base::TimeDelta::FromMilliseconds(300)) {
+                   base::TimeDelta::FromMilliseconds(300)),
+      idle_canceled_delayed_task_sweeper_("worker.scheduler",
+                                          &helper_,
+                                          idle_helper_.IdleTaskRunner()) {
   initialized_ = false;
   TRACE_EVENT_OBJECT_CREATED_WITH_ID(
       TRACE_DISABLED_BY_DEFAULT("worker.scheduler"), "WorkerScheduler", this);
diff --git a/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl.h
index a561b05..b6cf8d614 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_CHILD_WORKER_SCHEDULER_IMPL_H_
 
 #include "base/macros.h"
+#include "platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
 #include "platform/scheduler/child/idle_helper.h"
 #include "platform/scheduler/child/scheduler_helper.h"
 #include "public/platform/scheduler/child/worker_scheduler.h"
@@ -50,6 +51,7 @@
 
   SchedulerHelper helper_;
   IdleHelper idle_helper_;
+  IdleCanceledDelayedTaskSweeper idle_canceled_delayed_task_sweeper_;
   bool initialized_;
 
   DISALLOW_COPY_AND_ASSIGN(WorkerSchedulerImpl);
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
index 72652c8..130d539 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
@@ -93,6 +93,9 @@
                    TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
                    "RendererSchedulerIdlePeriod",
                    base::TimeDelta()),
+      idle_canceled_delayed_task_sweeper_("renderer.scheduler",
+                                          &helper_,
+                                          idle_helper_.IdleTaskRunner()),
       render_widget_scheduler_signals_(this),
       control_task_runner_(helper_.ControlTaskRunner()),
       compositor_task_runner_(
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
index fe9be1f..a401289 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
@@ -14,6 +14,7 @@
 #include "platform/scheduler/base/pollable_thread_safe_flag.h"
 #include "platform/scheduler/base/queueing_time_estimator.h"
 #include "platform/scheduler/base/thread_load_tracker.h"
+#include "platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
 #include "platform/scheduler/child/idle_helper.h"
 #include "platform/scheduler/child/scheduler_helper.h"
 #include "platform/scheduler/renderer/deadline_task_runner.h"
@@ -376,6 +377,7 @@
 
   SchedulerHelper helper_;
   IdleHelper idle_helper_;
+  IdleCanceledDelayedTaskSweeper idle_canceled_delayed_task_sweeper_;
   std::unique_ptr<TaskQueueThrottler> task_queue_throttler_;
   RenderWidgetSignals render_widget_scheduler_signals_;
 
diff --git a/third_party/WebKit/Source/web/WebAssociatedURLLoaderImpl.cpp b/third_party/WebKit/Source/web/WebAssociatedURLLoaderImpl.cpp
index 9254db0..a2afce29 100644
--- a/third_party/WebKit/Source/web/WebAssociatedURLLoaderImpl.cpp
+++ b/third_party/WebKit/Source/web/WebAssociatedURLLoaderImpl.cpp
@@ -413,7 +413,8 @@
     // TODO(yhirano): Remove this CHECK once https://crbug.com/667254 is fixed.
     CHECK(!m_loader);
     m_loader = DocumentThreadableLoader::create(
-        *document, m_clientAdapter.get(), options, resourceLoaderOptions);
+        *document, m_clientAdapter.get(), options, resourceLoaderOptions,
+        ThreadableLoader::ClientSpec::kWebAssociatedURLLoader);
     m_loader->start(webcoreRequest);
   }
 
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index 6665c81..e280ce9 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -9371,14 +9371,16 @@
   options.crossOriginRequestPolicy = UseAccessControl;
   ResourceLoaderOptions resourceLoaderOptions;
   DocumentThreadableLoader::loadResourceSynchronously(
-      *frame->document(), request, client, options, resourceLoaderOptions);
+      *frame->document(), request, client, options, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kTesting);
   EXPECT_TRUE(client.failed());
 
   client.reset();
   // Try to load the request with cross origin access. Should succeed.
   options.crossOriginRequestPolicy = AllowCrossOriginRequests;
   DocumentThreadableLoader::loadResourceSynchronously(
-      *frame->document(), request, client, options, resourceLoaderOptions);
+      *frame->document(), request, client, options, resourceLoaderOptions,
+      ThreadableLoader::ClientSpec::kTesting);
   EXPECT_FALSE(client.failed());
 }
 
diff --git a/third_party/WebKit/public/platform/scheduler/child/compositor_worker_scheduler.h b/third_party/WebKit/public/platform/scheduler/child/compositor_worker_scheduler.h
index 2a17690..e7418031 100644
--- a/third_party/WebKit/public/platform/scheduler/child/compositor_worker_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/child/compositor_worker_scheduler.h
@@ -42,6 +42,7 @@
   void OnIdleTaskPosted() override;
   base::TimeTicks WillProcessIdleTask() override;
   void DidProcessIdleTask() override;
+  base::TimeTicks NowTicks() override;
 
  private:
   base::Thread* thread_;
diff --git a/third_party/WebKit/public/platform/scheduler/child/single_thread_idle_task_runner.h b/third_party/WebKit/public/platform/scheduler/child/single_thread_idle_task_runner.h
index 3362c69b..4fd615d 100644
--- a/third_party/WebKit/public/platform/scheduler/child/single_thread_idle_task_runner.h
+++ b/third_party/WebKit/public/platform/scheduler/child/single_thread_idle_task_runner.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_WEBKIT_PUBLIC_PLATFORM_SCHEDULER_CHILD_SINGLE_THREAD_IDLE_TASK_RUNNER_H_
 #define THIRD_PARTY_WEBKIT_PUBLIC_PLATFORM_SCHEDULER_CHILD_SINGLE_THREAD_IDLE_TASK_RUNNER_H_
 
+#include <map>
+
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/macros.h"
@@ -22,6 +24,7 @@
 
 namespace blink {
 namespace scheduler {
+class IdleHelper;
 
 // A SingleThreadIdleTaskRunner is a task runner for running idle tasks. Idle
 // tasks have an unbound argument which is bound to a deadline
@@ -50,6 +53,9 @@
     // Signals that an idle task has finished being run.
     virtual void DidProcessIdleTask() = 0;
 
+    // Returns the current time.
+    virtual base::TimeTicks NowTicks() = 0;
+
    private:
     DISALLOW_COPY_AND_ASSIGN(Delegate);
   };
@@ -64,6 +70,13 @@
   virtual void PostIdleTask(const tracked_objects::Location& from_here,
                             const IdleTask& idle_task);
 
+  // |idle_task| is eligible to run after the next time an idle period starts
+  // after |delay|.  Note this has after wakeup semantics, i.e. unless something
+  // else wakes the CPU up, this won't run.
+  virtual void PostDelayedIdleTask(const tracked_objects::Location& from_here,
+                                   const base::TimeDelta delay,
+                                   const IdleTask& idle_task);
+
   virtual void PostNonNestableIdleTask(
       const tracked_objects::Location& from_here,
       const IdleTask& idle_task);
@@ -77,10 +90,17 @@
 
  private:
   friend class base::RefCountedThreadSafe<SingleThreadIdleTaskRunner>;
+  friend class IdleHelper;
 
   void RunTask(IdleTask idle_task);
 
+  void EnqueueReadyDelayedIdleTasks();
+
+  using DelayedIdleTask =
+      std::pair<const tracked_objects::Location, base::Closure>;
+
   scoped_refptr<base::SingleThreadTaskRunner> idle_priority_task_runner_;
+  std::multimap<base::TimeTicks, DelayedIdleTask> delayed_idle_tasks_;
   Delegate* delegate_;  // NOT OWNED
   const char* tracing_category_;
   base::trace_event::BlameContext* blame_context_;  // Not owned.
diff --git a/third_party/openh264/BUILD.gn b/third_party/openh264/BUILD.gn
index ad6c8a6..4301abd 100644
--- a/third_party/openh264/BUILD.gn
+++ b/third_party/openh264/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//third_party/openh264/openh264_args.gni")
+import("//third_party/openh264/openh264_sources.gni")
 
 # Config shared by all openh264 targets.
 config("config") {
@@ -28,58 +29,8 @@
 }
 
 source_set("common") {
-  sources = [
-    "src/codec/common/inc/WelsCircleQueue.h",
-    "src/codec/common/inc/WelsList.h",
-    "src/codec/common/inc/WelsLock.h",
-    "src/codec/common/inc/WelsTask.h",
-    "src/codec/common/inc/WelsTaskThread.h",
-    "src/codec/common/inc/WelsThread.h",
-    "src/codec/common/inc/WelsThreadLib.h",
-    "src/codec/common/inc/WelsThreadPool.h",
-    "src/codec/common/inc/copy_mb.h",
-    "src/codec/common/inc/cpu.h",
-    "src/codec/common/inc/cpu_core.h",
-    "src/codec/common/inc/crt_util_safe_x.h",
-    "src/codec/common/inc/deblocking_common.h",
-    "src/codec/common/inc/expand_pic.h",
-    "src/codec/common/inc/golomb_common.h",
-    "src/codec/common/inc/intra_pred_common.h",
-    "src/codec/common/inc/ls_defines.h",
-    "src/codec/common/inc/macros.h",
-    "src/codec/common/inc/mc.h",
-    "src/codec/common/inc/measure_time.h",
-    "src/codec/common/inc/memory_align.h",
-    "src/codec/common/inc/sad_common.h",
-    "src/codec/common/inc/typedefs.h",
-    "src/codec/common/inc/utils.h",
-    "src/codec/common/inc/version.h",
-    "src/codec/common/inc/welsCodecTrace.h",
-    "src/codec/common/inc/wels_common_defs.h",
-    "src/codec/common/inc/wels_const_common.h",
-    "src/codec/common/src/WelsTaskThread.cpp",
-    "src/codec/common/src/WelsThread.cpp",
-    "src/codec/common/src/WelsThreadLib.cpp",
-    "src/codec/common/src/WelsThreadPool.cpp",
-    "src/codec/common/src/common_tables.cpp",
-    "src/codec/common/src/copy_mb.cpp",
-    "src/codec/common/src/cpu.cpp",
-    "src/codec/common/src/crt_util_safe_x.cpp",
-    "src/codec/common/src/deblocking_common.cpp",
-    "src/codec/common/src/expand_pic.cpp",
-    "src/codec/common/src/intra_pred_common.cpp",
-    "src/codec/common/src/mc.cpp",
-    "src/codec/common/src/memory_align.cpp",
-    "src/codec/common/src/sad_common.cpp",
-    "src/codec/common/src/utils.cpp",
-    "src/codec/common/src/welsCodecTrace.cpp",
-  ]
-
-  include_dirs = [
-    "src/codec/api/svc",
-    "src/codec/common/inc",
-    "src/codec/common/src",
-  ]
+  sources = openh264_common_sources
+  include_dirs = openh264_common_include_dirs
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
@@ -97,60 +48,8 @@
 }
 
 source_set("processing") {
-  sources = [
-    "src/codec/processing/interface/IWelsVP.h",
-    "src/codec/processing/src/adaptivequantization/AdaptiveQuantization.cpp",
-    "src/codec/processing/src/adaptivequantization/AdaptiveQuantization.h",
-    "src/codec/processing/src/backgrounddetection/BackgroundDetection.cpp",
-    "src/codec/processing/src/backgrounddetection/BackgroundDetection.h",
-    "src/codec/processing/src/common/WelsFrameWork.cpp",
-    "src/codec/processing/src/common/WelsFrameWork.h",
-    "src/codec/processing/src/common/WelsFrameWorkEx.cpp",
-    "src/codec/processing/src/common/common.h",
-    "src/codec/processing/src/common/memory.cpp",
-    "src/codec/processing/src/common/memory.h",
-    "src/codec/processing/src/common/resource.h",
-    "src/codec/processing/src/common/typedef.h",
-    "src/codec/processing/src/common/util.h",
-    "src/codec/processing/src/complexityanalysis/ComplexityAnalysis.cpp",
-    "src/codec/processing/src/complexityanalysis/ComplexityAnalysis.h",
-    "src/codec/processing/src/denoise/denoise.cpp",
-    "src/codec/processing/src/denoise/denoise.h",
-    "src/codec/processing/src/denoise/denoise_filter.cpp",
-    "src/codec/processing/src/downsample/downsample.cpp",
-    "src/codec/processing/src/downsample/downsample.h",
-    "src/codec/processing/src/downsample/downsamplefuncs.cpp",
-    "src/codec/processing/src/imagerotate/imagerotate.cpp",
-    "src/codec/processing/src/imagerotate/imagerotate.h",
-    "src/codec/processing/src/imagerotate/imagerotatefuncs.cpp",
-    "src/codec/processing/src/scenechangedetection/SceneChangeDetection.cpp",
-    "src/codec/processing/src/scenechangedetection/SceneChangeDetection.h",
-    "src/codec/processing/src/scrolldetection/ScrollDetection.cpp",
-    "src/codec/processing/src/scrolldetection/ScrollDetection.h",
-    "src/codec/processing/src/scrolldetection/ScrollDetectionFuncs.cpp",
-    "src/codec/processing/src/scrolldetection/ScrollDetectionFuncs.h",
-    "src/codec/processing/src/vaacalc/vaacalcfuncs.cpp",
-    "src/codec/processing/src/vaacalc/vaacalculation.cpp",
-    "src/codec/processing/src/vaacalc/vaacalculation.h",
-  ]
-
-  include_dirs = [
-    "src/codec/api/svc",
-    "src/codec/common/inc",
-    "src/codec/common/src",
-    "src/codec/processing/interface",
-    "src/codec/processing/interface/",
-    "src/codec/processing/src/adaptivequantization",
-    "src/codec/processing/src/backgrounddetection",
-    "src/codec/processing/src/common",
-    "src/codec/processing/src/complexityanalysis",
-    "src/codec/processing/src/denoise",
-    "src/codec/processing/src/downsample",
-    "src/codec/processing/src/imagerotate",
-    "src/codec/processing/src/scenechangedetection",
-    "src/codec/processing/src/scrolldetection",
-    "src/codec/processing/src/vaacalc",
-  ]
+  sources = openh264_processing_sources
+  include_dirs = openh264_processing_include_dirs
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
@@ -161,103 +60,8 @@
 }
 
 source_set("encoder") {
-  sources = [
-    "src/codec/encoder/core/inc/as264_common.h",
-    "src/codec/encoder/core/inc/au_set.h",
-    "src/codec/encoder/core/inc/deblocking.h",
-    "src/codec/encoder/core/inc/decode_mb_aux.h",
-    "src/codec/encoder/core/inc/dq_map.h",
-    "src/codec/encoder/core/inc/encode_mb_aux.h",
-    "src/codec/encoder/core/inc/encoder.h",
-    "src/codec/encoder/core/inc/encoder_context.h",
-    "src/codec/encoder/core/inc/extern.h",
-    "src/codec/encoder/core/inc/get_intra_predictor.h",
-    "src/codec/encoder/core/inc/mb_cache.h",
-    "src/codec/encoder/core/inc/md.h",
-    "src/codec/encoder/core/inc/mt_defs.h",
-    "src/codec/encoder/core/inc/mv_pred.h",
-    "src/codec/encoder/core/inc/nal_encap.h",
-    "src/codec/encoder/core/inc/param_svc.h",
-    "src/codec/encoder/core/inc/parameter_sets.h",
-    "src/codec/encoder/core/inc/paraset_strategy.h",
-    "src/codec/encoder/core/inc/picture.h",
-    "src/codec/encoder/core/inc/picture_handle.h",
-    "src/codec/encoder/core/inc/rc.h",
-    "src/codec/encoder/core/inc/ref_list_mgr_svc.h",
-    "src/codec/encoder/core/inc/sample.h",
-    "src/codec/encoder/core/inc/set_mb_syn_cabac.h",
-    "src/codec/encoder/core/inc/set_mb_syn_cavlc.h",
-    "src/codec/encoder/core/inc/slice.h",
-    "src/codec/encoder/core/inc/slice_multi_threading.h",
-    "src/codec/encoder/core/inc/stat.h",
-    "src/codec/encoder/core/inc/svc_base_layer_md.h",
-    "src/codec/encoder/core/inc/svc_enc_frame.h",
-    "src/codec/encoder/core/inc/svc_enc_golomb.h",
-    "src/codec/encoder/core/inc/svc_enc_macroblock.h",
-    "src/codec/encoder/core/inc/svc_enc_slice_segment.h",
-    "src/codec/encoder/core/inc/svc_encode_mb.h",
-    "src/codec/encoder/core/inc/svc_encode_slice.h",
-    "src/codec/encoder/core/inc/svc_mode_decision.h",
-    "src/codec/encoder/core/inc/svc_motion_estimate.h",
-    "src/codec/encoder/core/inc/svc_set_mb_syn.h",
-    "src/codec/encoder/core/inc/svc_set_mb_syn_cavlc.h",
-    "src/codec/encoder/core/inc/vlc_encoder.h",
-    "src/codec/encoder/core/inc/wels_common_basis.h",
-    "src/codec/encoder/core/inc/wels_const.h",
-    "src/codec/encoder/core/inc/wels_func_ptr_def.h",
-    "src/codec/encoder/core/inc/wels_preprocess.h",
-    "src/codec/encoder/core/inc/wels_task_base.h",
-    "src/codec/encoder/core/inc/wels_task_encoder.h",
-    "src/codec/encoder/core/inc/wels_task_management.h",
-    "src/codec/encoder/core/inc/wels_transpose_matrix.h",
-    "src/codec/encoder/core/src/au_set.cpp",
-    "src/codec/encoder/core/src/deblocking.cpp",
-    "src/codec/encoder/core/src/decode_mb_aux.cpp",
-    "src/codec/encoder/core/src/encode_mb_aux.cpp",
-    "src/codec/encoder/core/src/encoder.cpp",
-    "src/codec/encoder/core/src/encoder_data_tables.cpp",
-    "src/codec/encoder/core/src/encoder_ext.cpp",
-    "src/codec/encoder/core/src/get_intra_predictor.cpp",
-    "src/codec/encoder/core/src/md.cpp",
-    "src/codec/encoder/core/src/mv_pred.cpp",
-    "src/codec/encoder/core/src/nal_encap.cpp",
-    "src/codec/encoder/core/src/paraset_strategy.cpp",
-    "src/codec/encoder/core/src/picture_handle.cpp",
-    "src/codec/encoder/core/src/ratectl.cpp",
-    "src/codec/encoder/core/src/ref_list_mgr_svc.cpp",
-    "src/codec/encoder/core/src/sample.cpp",
-    "src/codec/encoder/core/src/set_mb_syn_cabac.cpp",
-    "src/codec/encoder/core/src/set_mb_syn_cavlc.cpp",
-    "src/codec/encoder/core/src/slice_multi_threading.cpp",
-    "src/codec/encoder/core/src/svc_base_layer_md.cpp",
-    "src/codec/encoder/core/src/svc_enc_slice_segment.cpp",
-    "src/codec/encoder/core/src/svc_encode_mb.cpp",
-    "src/codec/encoder/core/src/svc_encode_slice.cpp",
-    "src/codec/encoder/core/src/svc_mode_decision.cpp",
-    "src/codec/encoder/core/src/svc_motion_estimate.cpp",
-    "src/codec/encoder/core/src/svc_set_mb_syn_cabac.cpp",
-    "src/codec/encoder/core/src/svc_set_mb_syn_cavlc.cpp",
-    "src/codec/encoder/core/src/wels_preprocess.cpp",
-    "src/codec/encoder/core/src/wels_task_base.cpp",
-    "src/codec/encoder/core/src/wels_task_encoder.cpp",
-    "src/codec/encoder/core/src/wels_task_management.cpp",
-    "src/codec/encoder/plus/inc/welsEncoderExt.h",
-    "src/codec/encoder/plus/src/welsEncoderExt.cpp",
-
-    # Note: Purposefully excluded: 'src/codec/encoder/plus/src/DllEntry.cpp',
-    # This file is not built by the OpenH264 original build files.
-  ]
-
-  include_dirs = [
-    "src/codec/api/svc",
-    "src/codec/common/inc",
-    "src/codec/common/src",
-    "src/codec/encoder/core/inc",
-    "src/codec/encoder/core/src",
-    "src/codec/encoder/plus/inc",
-    "src/codec/encoder/plus/src",
-    "src/codec/processing/interface",
-  ]
+  sources = openh264_encoder_sources
+  include_dirs = openh264_encoder_include_dirs
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
diff --git a/third_party/openh264/openh264_sources.gni b/third_party/openh264/openh264_sources.gni
new file mode 100644
index 0000000..6f8c4f5
--- /dev/null
+++ b/third_party/openh264/openh264_sources.gni
@@ -0,0 +1,289 @@
+# Common
+openh264_common_include_dirs = [
+  "//third_party/openh264/src/codec/api/svc",
+  "//third_party/openh264/src/codec/common/inc",
+  "//third_party/openh264/src/codec/common/src",
+]
+
+openh264_common_sources = [
+  "//third_party/openh264/src/codec/common/inc/WelsCircleQueue.h",
+  "//third_party/openh264/src/codec/common/inc/WelsList.h",
+  "//third_party/openh264/src/codec/common/inc/WelsLock.h",
+  "//third_party/openh264/src/codec/common/inc/WelsTask.h",
+  "//third_party/openh264/src/codec/common/inc/WelsTaskThread.h",
+  "//third_party/openh264/src/codec/common/inc/WelsThread.h",
+  "//third_party/openh264/src/codec/common/inc/WelsThreadLib.h",
+  "//third_party/openh264/src/codec/common/inc/WelsThreadPool.h",
+  "//third_party/openh264/src/codec/common/inc/copy_mb.h",
+  "//third_party/openh264/src/codec/common/inc/cpu.h",
+  "//third_party/openh264/src/codec/common/inc/cpu_core.h",
+  "//third_party/openh264/src/codec/common/inc/crt_util_safe_x.h",
+  "//third_party/openh264/src/codec/common/inc/deblocking_common.h",
+  "//third_party/openh264/src/codec/common/inc/expand_pic.h",
+  "//third_party/openh264/src/codec/common/inc/golomb_common.h",
+  "//third_party/openh264/src/codec/common/inc/intra_pred_common.h",
+  "//third_party/openh264/src/codec/common/inc/ls_defines.h",
+  "//third_party/openh264/src/codec/common/inc/macros.h",
+  "//third_party/openh264/src/codec/common/inc/mc.h",
+  "//third_party/openh264/src/codec/common/inc/measure_time.h",
+  "//third_party/openh264/src/codec/common/inc/memory_align.h",
+  "//third_party/openh264/src/codec/common/inc/sad_common.h",
+  "//third_party/openh264/src/codec/common/inc/typedefs.h",
+  "//third_party/openh264/src/codec/common/inc/utils.h",
+  "//third_party/openh264/src/codec/common/inc/version.h",
+  "//third_party/openh264/src/codec/common/inc/welsCodecTrace.h",
+  "//third_party/openh264/src/codec/common/inc/wels_common_defs.h",
+  "//third_party/openh264/src/codec/common/inc/wels_const_common.h",
+  "//third_party/openh264/src/codec/common/src/WelsTaskThread.cpp",
+  "//third_party/openh264/src/codec/common/src/WelsThread.cpp",
+  "//third_party/openh264/src/codec/common/src/WelsThreadLib.cpp",
+  "//third_party/openh264/src/codec/common/src/WelsThreadPool.cpp",
+  "//third_party/openh264/src/codec/common/src/common_tables.cpp",
+  "//third_party/openh264/src/codec/common/src/copy_mb.cpp",
+  "//third_party/openh264/src/codec/common/src/cpu.cpp",
+  "//third_party/openh264/src/codec/common/src/crt_util_safe_x.cpp",
+  "//third_party/openh264/src/codec/common/src/deblocking_common.cpp",
+  "//third_party/openh264/src/codec/common/src/expand_pic.cpp",
+  "//third_party/openh264/src/codec/common/src/intra_pred_common.cpp",
+  "//third_party/openh264/src/codec/common/src/mc.cpp",
+  "//third_party/openh264/src/codec/common/src/memory_align.cpp",
+  "//third_party/openh264/src/codec/common/src/sad_common.cpp",
+  "//third_party/openh264/src/codec/common/src/utils.cpp",
+  "//third_party/openh264/src/codec/common/src/welsCodecTrace.cpp",
+]
+
+openh264_common_sources_asm_x86 = [
+  "//third_party/openh264/src/codec/common/x86/asm_inc.asm",
+  "//third_party/openh264/src/codec/common/x86/cpuid.asm",
+  "//third_party/openh264/src/codec/common/x86/dct.asm",
+  "//third_party/openh264/src/codec/common/x86/deblock.asm",
+  "//third_party/openh264/src/codec/common/x86/expand_picture.asm",
+  "//third_party/openh264/src/codec/common/x86/intra_pred_com.asm",
+  "//third_party/openh264/src/codec/common/x86/mb_copy.asm",
+  "//third_party/openh264/src/codec/common/x86/mc_chroma.asm",
+  "//third_party/openh264/src/codec/common/x86/mc_luma.asm",
+  "//third_party/openh264/src/codec/common/x86/satd_sad.asm",
+  "//third_party/openh264/src/codec/common/x86/vaa.asm",
+]
+
+openh264_common_sources_asm_arm = [
+  "//third_party/openh264/src/codec/common/arm/arm_arch_common_macro.S",
+  "//third_party/openh264/src/codec/common/arm/copy_mb_neon.S",
+  "//third_party/openh264/src/codec/common/arm/deblocking_neon.S",
+  "//third_party/openh264/src/codec/common/arm/intra_pred_common_neo.S",
+  "//third_party/openh264/src/codec/common/arm/mc_neon.S",
+]
+
+openh264_common_sources_asm_arm64 = [
+  "//third_party/openh264/src/codec/common/arm64/arm_arch64_common_macro.S",
+  "//third_party/openh264/src/codec/common/arm64/copy_mb_aarch64_neon.S",
+  "//third_party/openh264/src/codec/common/arm64/deblocking_aarch64_neon.S",
+  "//third_party/openh264/src/codec/common/arm64/expand_picture_aarch64_neon.S",
+  "//third_party/openh264/src/codec/common/arm64/intra_pred_common_aarch64_neon.S",
+  "//third_party/openh264/src/codec/common/arm64/mc_aarch64_neon.S",
+]
+
+# Processing
+openh264_processing_include_dirs = [
+  "//third_party/openh264/src/codec/api/svc",
+  "//third_party/openh264/src/codec/common/inc",
+  "//third_party/openh264/src/codec/common/src",
+  "//third_party/openh264/src/codec/common/x86",
+  "//third_party/openh264/src/codec/processing/interface",
+  "//third_party/openh264/src/codec/processing/src/adaptivequantization",
+  "//third_party/openh264/src/codec/processing/src/backgrounddetection",
+  "//third_party/openh264/src/codec/processing/src/common",
+  "//third_party/openh264/src/codec/processing/src/complexityanalysis",
+  "//third_party/openh264/src/codec/processing/src/denoise",
+  "//third_party/openh264/src/codec/processing/src/downsample",
+  "//third_party/openh264/src/codec/processing/src/imagerotate",
+  "//third_party/openh264/src/codec/processing/src/scenechangedetection",
+  "//third_party/openh264/src/codec/processing/src/scrolldetection",
+  "//third_party/openh264/src/codec/processing/src/vaacalc",
+]
+
+openh264_processing_sources = [
+  "//third_party/openh264/src/codec/processing/interface/IWelsVP.h",
+  "//third_party/openh264/src/codec/processing/src/adaptivequantization/AdaptiveQuantization.cpp",
+  "//third_party/openh264/src/codec/processing/src/adaptivequantization/AdaptiveQuantization.h",
+  "//third_party/openh264/src/codec/processing/src/backgrounddetection/BackgroundDetection.cpp",
+  "//third_party/openh264/src/codec/processing/src/backgrounddetection/BackgroundDetection.h",
+  "//third_party/openh264/src/codec/processing/src/common/WelsFrameWork.cpp",
+  "//third_party/openh264/src/codec/processing/src/common/WelsFrameWork.h",
+  "//third_party/openh264/src/codec/processing/src/common/WelsFrameWorkEx.cpp",
+  "//third_party/openh264/src/codec/processing/src/common/common.h",
+  "//third_party/openh264/src/codec/processing/src/common/memory.cpp",
+  "//third_party/openh264/src/codec/processing/src/common/memory.h",
+  "//third_party/openh264/src/codec/processing/src/common/resource.h",
+  "//third_party/openh264/src/codec/processing/src/common/typedef.h",
+  "//third_party/openh264/src/codec/processing/src/common/util.h",
+  "//third_party/openh264/src/codec/processing/src/complexityanalysis/ComplexityAnalysis.cpp",
+  "//third_party/openh264/src/codec/processing/src/complexityanalysis/ComplexityAnalysis.h",
+  "//third_party/openh264/src/codec/processing/src/denoise/denoise.cpp",
+  "//third_party/openh264/src/codec/processing/src/denoise/denoise.h",
+  "//third_party/openh264/src/codec/processing/src/denoise/denoise_filter.cpp",
+  "//third_party/openh264/src/codec/processing/src/downsample/downsample.cpp",
+  "//third_party/openh264/src/codec/processing/src/downsample/downsample.h",
+  "//third_party/openh264/src/codec/processing/src/downsample/downsamplefuncs.cpp",
+  "//third_party/openh264/src/codec/processing/src/imagerotate/imagerotate.cpp",
+  "//third_party/openh264/src/codec/processing/src/imagerotate/imagerotate.h",
+  "//third_party/openh264/src/codec/processing/src/imagerotate/imagerotatefuncs.cpp",
+  "//third_party/openh264/src/codec/processing/src/scenechangedetection/SceneChangeDetection.cpp",
+  "//third_party/openh264/src/codec/processing/src/scenechangedetection/SceneChangeDetection.h",
+  "//third_party/openh264/src/codec/processing/src/scrolldetection/ScrollDetection.cpp",
+  "//third_party/openh264/src/codec/processing/src/scrolldetection/ScrollDetection.h",
+  "//third_party/openh264/src/codec/processing/src/scrolldetection/ScrollDetectionFuncs.cpp",
+  "//third_party/openh264/src/codec/processing/src/scrolldetection/ScrollDetectionFuncs.h",
+  "//third_party/openh264/src/codec/processing/src/vaacalc/vaacalcfuncs.cpp",
+  "//third_party/openh264/src/codec/processing/src/vaacalc/vaacalculation.cpp",
+  "//third_party/openh264/src/codec/processing/src/vaacalc/vaacalculation.h",
+]
+
+openh264_processing_sources_asm_x86 = [
+  "//third_party/openh264/src/codec/processing/src/x86/denoisefilter.asm",
+  "//third_party/openh264/src/codec/processing/src/x86/downsample_bilinear.asm",
+  "//third_party/openh264/src/codec/processing/src/x86/vaa.asm",
+]
+
+openh264_processing_sources_asm_arm = [
+  "//third_party/openh264/src/codec/processing/src/arm/adaptive_quantization.S",
+  "//third_party/openh264/src/codec/processing/src/arm/down_sample_neon.S",
+  "//third_party/openh264/src/codec/processing/src/arm/pixel_sad_neon.S",
+  "//third_party/openh264/src/codec/processing/src/arm/vaa_calc_neon.S",
+]
+
+openh264_processing_sources_asm_arm64 = [
+  "//third_party/openh264/src/codec/processing/src/arm64/adaptive_quantization_aarch64_neon.S",
+  "//third_party/openh264/src/codec/processing/src/arm64/down_sample_aarch64_neon.S",
+  "//third_party/openh264/src/codec/processing/src/arm64/pixel_sad_aarch64_neon.S",
+  "//third_party/openh264/src/codec/processing/src/arm64/vaa_calc_aarch64_neon.S",
+]
+
+# Encoder
+openh264_encoder_include_dirs = [
+  "//third_party/openh264/src/codec/api/svc",
+  "//third_party/openh264/src/codec/common/inc",
+  "//third_party/openh264/src/codec/common/src",
+  "//third_party/openh264/src/codec/common/x86",
+  "//third_party/openh264/src/codec/encoder/core/inc",
+  "//third_party/openh264/src/codec/encoder/core/src",
+  "//third_party/openh264/src/codec/encoder/plus/inc",
+  "//third_party/openh264/src/codec/encoder/plus/src",
+  "//third_party/openh264/src/codec/processing/interface",
+]
+
+openh264_encoder_sources = [
+  "//third_party/openh264/src/codec/encoder/core/inc/as264_common.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/au_set.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/deblocking.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/decode_mb_aux.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/dq_map.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/encode_mb_aux.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/encoder.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/encoder_context.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/extern.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/get_intra_predictor.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/mb_cache.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/md.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/mt_defs.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/mv_pred.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/nal_encap.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/param_svc.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/parameter_sets.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/paraset_strategy.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/picture.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/picture_handle.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/rc.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/ref_list_mgr_svc.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/sample.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/set_mb_syn_cabac.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/set_mb_syn_cavlc.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/slice.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/slice_multi_threading.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/stat.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_base_layer_md.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_enc_frame.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_enc_golomb.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_enc_macroblock.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_enc_slice_segment.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_encode_mb.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_encode_slice.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_mode_decision.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_motion_estimate.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_set_mb_syn.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/svc_set_mb_syn_cavlc.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/vlc_encoder.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/wels_common_basis.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/wels_const.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/wels_func_ptr_def.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/wels_preprocess.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/wels_task_base.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/wels_task_encoder.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/wels_task_management.h",
+  "//third_party/openh264/src/codec/encoder/core/inc/wels_transpose_matrix.h",
+  "//third_party/openh264/src/codec/encoder/core/src/au_set.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/deblocking.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/decode_mb_aux.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/encode_mb_aux.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/encoder.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/encoder_data_tables.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/encoder_ext.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/get_intra_predictor.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/md.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/mv_pred.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/nal_encap.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/paraset_strategy.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/picture_handle.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/ratectl.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/ref_list_mgr_svc.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/sample.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/set_mb_syn_cabac.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/set_mb_syn_cavlc.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/slice_multi_threading.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/svc_base_layer_md.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/svc_enc_slice_segment.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/svc_encode_mb.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/svc_encode_slice.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/svc_mode_decision.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/svc_motion_estimate.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/svc_set_mb_syn_cabac.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/svc_set_mb_syn_cavlc.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/wels_preprocess.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/wels_task_base.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/wels_task_encoder.cpp",
+  "//third_party/openh264/src/codec/encoder/core/src/wels_task_management.cpp",
+  "//third_party/openh264/src/codec/encoder/plus/inc/welsEncoderExt.h",
+  "//third_party/openh264/src/codec/encoder/plus/src/welsEncoderExt.cpp",
+
+  # Note: Purposefully excluded: 'src/codec/encoder/plus/src/DllEntry.cpp',
+  # This file is not built by the OpenH264 original build files.
+]
+
+openh264_encoder_sources_asm_x86 = [
+  "//third_party/openh264/src/codec/encoder/core/x86/coeff.asm",
+  "//third_party/openh264/src/codec/encoder/core/x86/dct.asm",
+  "//third_party/openh264/src/codec/encoder/core/x86/intra_pred.asm",
+  "//third_party/openh264/src/codec/encoder/core/x86/matrix_transpose.asm",
+  "//third_party/openh264/src/codec/encoder/core/x86/memzero.asm",
+  "//third_party/openh264/src/codec/encoder/core/x86/quant.asm",
+  "//third_party/openh264/src/codec/encoder/core/x86/sample_sc.asm",
+  "//third_party/openh264/src/codec/encoder/core/x86/score.asm",
+]
+
+openh264_encoder_sources_asm_arm = [
+  "//third_party/openh264/src/codec/encoder/core/arm/intra_pred_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm/intra_pred_sad_3_opt_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm/memory_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm/pixel_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm/reconstruct_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm/svc_motion_estimation.S",
+]
+
+openh264_encoder_sources_asm_arm64 = [
+  "//third_party/openh264/src/codec/encoder/core/arm64/intra_pred_aarch64_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm64/intra_pred_sad_3_opt_aarch64_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm64/memory_aarch64_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm64/pixel_aarch64_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm64/reconstruct_aarch64_neon.S",
+  "//third_party/openh264/src/codec/encoder/core/arm64/svc_motion_estimation_aarch64_neon.S",
+]
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index 35ecd10..cb390ca 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -673,7 +673,7 @@
   var imagePromise = this.thumbnailModel_.get([entry]).then(function(metadata) {
     return new Promise(function(fulfill, reject) {
       var loader = new ThumbnailLoader(
-          entry, ThumbnailLoader.LoaderType.IMAGE, metadata[0]);
+          entry, ThumbnailLoader.LoaderType.CANVAS, metadata[0]);
       loader.loadDetachedImage(function(result) {
         if (result)
           fulfill(loader.getImage());
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
index a5a090a..65d012f 100644
--- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
+++ b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
@@ -324,6 +324,46 @@
 };
 
 /**
+ * Renders an Image to canvas applying an image transformation.
+ * @param {Image} image Original image. Can be an unattached one to an elment.
+ * @param {number} width width of the original image
+ * @param {number} height height of the original image
+ * @param {{scaleX:number, scaleY:number, rotate90: number}} transform
+ *     Transform.
+ * @param {!Object} canvas
+ */
+ThumbnailLoader.prototype.renderImageToCanvasWithTransform_ = function(
+    image, width, height, transform, canvas) {
+  var scaleX = transform.scaleX;
+  var scaleY = transform.scaleY;
+  var rotate90 = transform.rotate90;
+
+  assert(scaleX === 1 || scaleX === -1);
+  assert(scaleY === 1 || scaleY === -1);
+  assert(rotate90 === 0 || rotate90 === 1);
+
+  var context = canvas.getContext('2d');
+
+  canvas.width = rotate90 === 1 ? height : width;
+  canvas.height = rotate90 === 1 ? width : height;
+
+  // Scale transformation should be applied before rotate transformation.
+  // i.e. When matrices for scale and rotate are A and B, transformation matrix
+  // should be BA.
+
+  // Rotate 90 degree at center.
+  if (rotate90 === 1) {
+    context.translate(height, 0);
+    context.rotate(Math.PI / 2);
+  }
+
+  // Flip X and Y.
+  context.translate(scaleX === -1 ? width : 0, scaleY === -1 ? height : 0);
+  context.scale(scaleX, scaleY);
+  context.drawImage(image, 0, 0);
+};
+
+/**
  * Applies transform to data url.
  *
  * @param {{scaleX:number, scaleY:number, rotate90: number}} transform
@@ -338,13 +378,6 @@
 ThumbnailLoader.prototype.applyTransformToDataUrl_ = function(
     transform, dataUrl, width, height) {
   var image = new Image();
-  var scaleX = this.transform_.scaleX;
-  var scaleY = this.transform_.scaleY;
-  var rotate90 = this.transform_.rotate90;
-
-  assert(scaleX === 1 || scaleX === -1);
-  assert(scaleY === 1 || scaleY === -1);
-  assert(rotate90 === 0 || rotate90 === 1);
 
   return new Promise(function(resolve, reject) {
     // Decode image for transformation.
@@ -352,27 +385,9 @@
     image.onerror = reject;
     image.src = dataUrl;
   }).then(function() {
-    // Apply transform. Scale transformation should be applied before rotate
-    // transformation. i.e. When matrices for scale and rotate are A and B,
-    // transformation matrix should be BA.
     var canvas = document.createElement('canvas');
-    var context = canvas.getContext('2d');
-
-    canvas.width = rotate90 === 1 ? height : width;
-    canvas.height = rotate90 === 1 ? width : height;
-
-    // Rotate 90 degree at center.
-    if (rotate90 === 1) {
-      context.translate(height, 0);
-      context.rotate(Math.PI / 2);
-    }
-
-    // Flip X and Y.
-    context.translate(scaleX === -1 ? width : 0, scaleY === -1 ? height : 0);
-    context.scale(scaleX, scaleY);
-
-    context.drawImage(image, 0, 0);
-
+    this.renderImageToCanvasWithTransform_(
+        image, width, height, this.transform_, canvas);
     return {
       data: canvas.toDataURL('image/png'),
       width: canvas.width,
@@ -473,6 +488,8 @@
     this.canvas_ = document.createElement('canvas');
 
   // Copy the image to a canvas if the canvas is outdated.
+  // At this point, image transformation is not applied because we attach style
+  // attribute to an img element in attachImage() instead.
   if (!this.canvasUpToDate_) {
     this.canvas_.width = this.image_.width;
     this.canvas_.height = this.image_.height;
@@ -521,14 +538,22 @@
 
 /**
  * Gets the loaded image.
- * TODO(mtomasz): Apply transformations.
  *
  * @return {Image|HTMLCanvasElement} Either image or a canvas object.
  */
 ThumbnailLoader.prototype.getImage = function() {
   this.renderMedia_();
-  return this.loaderType_ === ThumbnailLoader.LoaderType.CANVAS ? this.canvas_ :
-      this.image_;
+  if (this.loaderType_ === ThumbnailLoader.LoaderType.IMAGE)
+    // TODO(yamaguchi): Fix image orientation in case of detached image loaded
+    // in IMAGE mode. Either apply transformation here or return
+    // this.transform_ as well.
+    return this.image_;
+  if (this.transform_) {
+    this.renderImageToCanvasWithTransform_(
+        this.image_, this.image_.width, this.image_.height,
+        this.transform_, this.canvas_);
+  }
+  return this.canvas_;
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js b/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js
index 75129e461..f972dd1 100644
--- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js
@@ -168,3 +168,40 @@
         assertEquals(externalThumbnailDataUrl, result.data);
       }), callback);
 }
+
+function testLoadDetachedFromExifInCavnasModeThumbnailRotate(callback) {
+  ImageLoaderClient.getInstance = function() {
+    return {
+      load: function(url, callback, opt_option) {
+        // Assert that data url is passed.
+        assertTrue(/^data:/i.test(url));
+        callback({status: 'success', data: url, width: 64, height: 32});
+      }
+    };
+  };
+
+  var metadata = {
+    thumbnail: {
+      url: generateSampleImageDataUrl(64, 32),
+      transform: {
+        rotate90: 1,
+        scaleX: 1,
+        scaleY: -1,
+      }
+    }
+  };
+
+  var fileSystem = new MockFileSystem('volume-id');
+  var entry = new MockEntry(fileSystem, '/Test1.jpg');
+  var thumbnailLoader =
+      new ThumbnailLoader(entry, ThumbnailLoader.LoaderType.CANVAS, metadata);
+
+  reportPromise(
+    new Promise(function(resolve, reject) {
+      thumbnailLoader.loadDetachedImage(resolve);
+    }).then(function() {
+      var image = thumbnailLoader.getImage();
+      assertEquals(32, image.width);
+      assertEquals(64, image.height);
+    }), callback);
+}
diff --git a/ui/file_manager/gallery/js/gallery_item.js b/ui/file_manager/gallery/js/gallery_item.js
index 53c12eb..6829538b 100644
--- a/ui/file_manager/gallery/js/gallery_item.js
+++ b/ui/file_manager/gallery/js/gallery_item.js
@@ -440,3 +440,27 @@
     this.entry_ = entry;
   }.bind(this));
 };
+
+/**
+ * The threshold size of an image in pixels, which we always use thumbnail
+ * image for slide-in animation above this. This is a hack to avoid an UI
+ * unresponsiveness when switching between images.
+ * @type {number}
+ * @const
+ */
+GalleryItem.HEAVY_RENDERING_THRESHOLD_PIXELS = 4000 * 3000;
+
+/**
+ * Whether the image requires long rendering time.
+ *
+ * @return {boolean}
+ */
+GalleryItem.prototype.requireLongRenderingTime = function() {
+  // Check for undefined values.
+  if (!this.metadataItem_ ||
+      !this.metadataItem_.imageHeight || !this.metadataItem_.imageWidth)
+    return false;
+  var numPixels = this.metadataItem_.imageHeight *
+      this.metadataItem_.imageWidth;
+  return numPixels > GalleryItem.HEAVY_RENDERING_THRESHOLD_PIXELS;
+};
diff --git a/ui/file_manager/gallery/js/image_editor/image_view.js b/ui/file_manager/gallery/js/image_editor/image_view.js
index 0ee887f..f97c899 100644
--- a/ui/file_manager/gallery/js/image_editor/image_view.js
+++ b/ui/file_manager/gallery/js/image_editor/image_view.js
@@ -138,7 +138,7 @@
  * @return {ImageView.LoadTarget} Load target.
  */
 ImageView.getLoadTarget = function(item, effect) {
-  if (item.contentImage)
+  if (item.contentImage && !item.requireLongRenderingTime())
     return ImageView.LoadTarget.CACHED_MAIN_IMAGE;
 
   // Only show thumbnails if there is no effect or the effect is Slide or