diff --git a/DEPS b/DEPS
index 9076ffa..30c001f 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': '626575112bf0129d6cdf4ea5c843a2231a84fbbb',
+  'v8_revision': '4b8a39354acd5949f3a389581fe6bda679a4e6d2',
   # 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.
@@ -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' + '@' + 'ee5b9eaa30f9734c14c2d35130518b48da7eace6', # commit position 16098
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '3e52797e71d77b2d61bb31bf035314c030d0cfcc', # commit position 16104
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
diff --git a/chrome/VERSION b/chrome/VERSION
index e6e5fc0..c04a571f 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=57
 MINOR=0
-BUILD=2984
+BUILD=2985
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 792c49c..9fea3db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -959,6 +959,8 @@
             };
 
             mMainTab = null;
+            // mHasCreatedTabEarly == true => mMainTab != null in the rest of the code.
+            mHasCreatedTabEarly = false;
             tab.detachAndStartReparenting(intent, startActivityOptions, finalizeCallback);
         } else {
             // Temporarily allowing disk access while fixing. TODO: http://crbug.com/581860
diff --git a/chrome/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h
index aea1b61..79d79a2 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.h
+++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -10,7 +10,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/browsing_data/browsing_data_remover_delegate.h"
-#include "chrome/common/features.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // BrowsingDataRemover is responsible for removing data related to browsing:
diff --git a/chrome/browser/browsing_data/browsing_data_remover_impl.cc b/chrome/browser/browsing_data/browsing_data_remover_impl.cc
index 927a3e9..58c7550 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_impl.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_impl.cc
@@ -32,8 +32,6 @@
 #include "content/public/browser/plugin_data_remover.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/user_metrics.h"
-#include "extensions/features/features.h"
-#include "media/media_features.h"
 #include "net/base/net_errors.h"
 #include "net/cookies/cookie_store.h"
 #include "net/http/http_network_session.h"
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
index 74b8913e..86af547 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
@@ -61,6 +61,7 @@
 constexpr char kUnmanagedAuthToken[] = "unmanaged-auth-token";
 constexpr char kWellKnownConsumerName[] = "test@gmail.com";
 constexpr char kFakeUserName[] = "test@example.com";
+constexpr char kFakeGaiaId[] = "1234567890";
 
 }  // namespace
 
@@ -183,7 +184,7 @@
     profile()->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, true);
 
     const AccountId account_id(
-        AccountId::FromUserEmailGaiaId(kFakeUserName, "1234567890"));
+        AccountId::FromUserEmailGaiaId(kFakeUserName, kFakeGaiaId));
     GetFakeUserManager()->AddUser(account_id);
     GetFakeUserManager()->LoginUser(account_id);
 
@@ -192,6 +193,13 @@
   }
 
   void TearDownOnMainThread() override {
+    // Explicitly removing the user is required; otherwise ProfileHelper keeps
+    // a dangling pointer to the User.
+    // TODO(nya): Consider removing all users from ProfileHelper in the
+    // destructor of FakeChromeUserManager.
+    const AccountId account_id(
+        AccountId::FromUserEmailGaiaId(kFakeUserName, kFakeGaiaId));
+    GetFakeUserManager()->RemoveUserFromList(account_id);
     ArcSessionManager::Get()->Shutdown();
     ArcServiceManager::Get()->Shutdown();
     profile_.reset();
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_media_view_util.cc b/chrome/browser/chromeos/arc/fileapi/arc_media_view_util.cc
index 261516ff..999c676a 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_media_view_util.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_media_view_util.cc
@@ -13,7 +13,7 @@
 }  // namespace
 
 const base::Feature kMediaViewFeature{"ArcMediaView",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 const char kMediaDocumentsProviderAuthority[] =
     "com.android.providers.media.documents";
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index 00eadabc..219a2405c 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -1218,7 +1218,12 @@
             popup_model->result().default_match()->destination_url.spec());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DeleteItem) {
+#if defined(OS_WIN)
+#define MAYBE_DeleteItem DISABLED_DeleteItem
+#else
+#define MAYBE_DeleteItem DeleteItem
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_DeleteItem) {
   // Disable the search provider, to make sure the popup contains only history
   // items.
   TemplateURLService* model =
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index ed51898..c6c0c1bf 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9189.0.0
\ No newline at end of file
+9191.0.0
\ No newline at end of file
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker.cc b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
index 3fe59a8..8b78279 100644
--- a/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
+++ b/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
@@ -9,9 +9,11 @@
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
+#include "components/ntp_snippets/features.h"
 #include "components/ntp_snippets/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
 
 namespace ntp_snippets {
 
@@ -56,11 +58,19 @@
 const int kDecayFactorDenominator = 100;  // pow(0.91, 7) = 0.517
 
 // Number of positions by which a dismissed category is downgraded.
-const int kDismissedCategoryPenalty = 1;
+const int kDefaultDismissedCategoryPenalty = 1;
+const char* kDismissedCategoryPenaltyParamName =
+    "click_based_category_ranker-dismissed_category_penalty";
 
 const char kCategoryIdKey[] = "category";
 const char kClicksKey[] = "clicks";
 
+int GetDismissedCategoryPenaltyVariationValue() {
+  return variations::GetVariationParamByFeatureAsInt(
+      kCategoryRanker, kDismissedCategoryPenaltyParamName,
+      kDefaultDismissedCategoryPenalty);
+}
+
 }  // namespace
 
 ClickBasedCategoryRanker::ClickBasedCategoryRanker(
@@ -188,8 +198,14 @@
     return;
   }
 
+  const int penalty = GetDismissedCategoryPenaltyVariationValue();
+  if (penalty == 0) {
+    // The dismissed category penalty is turned off, the call is ignored.
+    return;
+  }
+
   std::vector<RankedCategory>::iterator current = FindCategory(category);
-  for (int downgrade = 0; downgrade < kDismissedCategoryPenalty; ++downgrade) {
+  for (int downgrade = 0; downgrade < penalty; ++downgrade) {
     std::vector<RankedCategory>::iterator next = current + 1;
     if (next == ordered_categories_.end()) {
       break;
@@ -232,7 +248,7 @@
 
 // static
 int ClickBasedCategoryRanker::GetDismissedCategoryPenalty() {
-  return kDismissedCategoryPenalty;
+  return GetDismissedCategoryPenaltyVariationValue();
 }
 
 ClickBasedCategoryRanker::RankedCategory::RankedCategory(Category category,
diff --git a/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc b/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
index 1956fea..7c19013 100644
--- a/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
+++ b/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
@@ -5,12 +5,16 @@
 #include "components/ntp_snippets/category_rankers/click_based_category_ranker.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "components/ntp_snippets/category.h"
 #include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/ntp_snippets_constants.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/variations/variations_params_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -65,12 +69,21 @@
     ranker()->OnCategoryDismissed(category);
   }
 
+  void SetDismissedCategoryPenaltyVariationParam(int value) {
+    variation_params_manager_.SetVariationParamsWithFeatureAssociations(
+        ntp_snippets::kStudyName,
+        {{"click_based_category_ranker-dismissed_category_penalty",
+          base::IntToString(value)}},
+        {kCategoryRanker.name});
+  }
+
   ClickBasedCategoryRanker* ranker() { return ranker_.get(); }
 
  private:
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   int unused_remote_category_id_;
   std::unique_ptr<ClickBasedCategoryRanker> ranker_;
+  variations::testing::VariationParamsManager variation_params_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRankerTest);
 };
@@ -324,6 +337,8 @@
 }
 
 TEST_F(ClickBasedCategoryRankerTest, ShouldMoveCategoryDownWhenDismissed) {
+  SetDismissedCategoryPenaltyVariationParam(2);
+
   // Take top categories.
   std::vector<KnownCategories> default_order =
       ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
@@ -337,6 +352,8 @@
 
 TEST_F(ClickBasedCategoryRankerTest,
        ShouldMoveSecondToLastCategoryDownWhenDismissed) {
+  SetDismissedCategoryPenaltyVariationParam(2);
+
   // Add categories to the bottom.
   Category first = AddUnusedRemoteCategory();
   Category second = AddUnusedRemoteCategory();
@@ -348,6 +365,8 @@
 
 TEST_F(ClickBasedCategoryRankerTest,
        ShouldNotMoveCategoryTooMuchDownWhenDismissed) {
+  SetDismissedCategoryPenaltyVariationParam(2);
+
   // Add enough categories to the end.
   std::vector<Category> categories;
   const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty();
@@ -377,6 +396,8 @@
 
 TEST_F(ClickBasedCategoryRankerTest,
        ShouldNotChangeOrderOfOtherCategoriesWhenDismissed) {
+  SetDismissedCategoryPenaltyVariationParam(2);
+
   // Add enough categories to the end.
   std::vector<Category> categories;
   const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty();
@@ -399,6 +420,8 @@
 }
 
 TEST_F(ClickBasedCategoryRankerTest, ShouldNotMoveLastCategoryWhenDismissed) {
+  SetDismissedCategoryPenaltyVariationParam(2);
+
   Category first = AddUnusedRemoteCategory();
   Category second = AddUnusedRemoteCategory();
 
@@ -409,6 +432,8 @@
 
 TEST_F(ClickBasedCategoryRankerTest,
        ShouldReduceLastCategoryClicksWhenDismissed) {
+  SetDismissedCategoryPenaltyVariationParam(2);
+
   Category first = AddUnusedRemoteCategory();
   Category second = AddUnusedRemoteCategory();
 
@@ -432,6 +457,46 @@
   EXPECT_FALSE(CompareCategories(first, second));
 }
 
+TEST_F(ClickBasedCategoryRankerTest,
+       ShouldTakeVariationValueForDismissedCategoryPenalty) {
+  const int penalty = 10203;
+  SetDismissedCategoryPenaltyVariationParam(penalty);
+  EXPECT_EQ(penalty, ClickBasedCategoryRanker::GetDismissedCategoryPenalty());
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+       ShouldDoNothingWhenCategoryDismissedIfPenaltyIsZero) {
+  SetDismissedCategoryPenaltyVariationParam(0);
+
+  // Add dummy remote categories to ensure that the following categories are not
+  // in the top anymore.
+  AddUnusedRemoteCategories(
+      ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+  Category first = AddUnusedRemoteCategory();
+  Category second = AddUnusedRemoteCategory();
+  Category third = AddUnusedRemoteCategory();
+
+  NotifyOnSuggestionOpened(
+      /*times=*/1, second);
+
+  // This should be ignored, because the penalty is set to 0.
+  NotifyOnCategoryDismissed(second);
+
+  // The second category should stay where it was.
+  EXPECT_TRUE(CompareCategories(first, second));
+  EXPECT_TRUE(CompareCategories(second, third));
+
+  // Try to move the second category up assuming that the previous click is
+  // still there.
+  NotifyOnSuggestionOpened(
+      /*times=*/ClickBasedCategoryRanker::GetPassingMargin() - 1, second);
+
+  // It should overtake the first category, because the dismissal should be
+  // ignored and the click should remain.
+  EXPECT_FALSE(CompareCategories(first, second));
+}
+
 TEST_F(ClickBasedCategoryRankerTest, ShouldRestoreDefaultOrderOnClearHistory) {
   std::vector<KnownCategories> default_order =
       ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
diff --git a/content/browser/service_worker/service_worker_context_request_handler.cc b/content/browser/service_worker/service_worker_context_request_handler.cc
index ed72f5f..520e5b61 100644
--- a/content/browser/service_worker/service_worker_context_request_handler.cc
+++ b/content/browser/service_worker/service_worker_context_request_handler.cc
@@ -65,18 +65,16 @@
   switch (status) {
     case CreateJobStatus::UNINITIALIZED:
       return "UNINITIALIZED";
-    case CreateJobStatus::WRITE_JOB_FOR_REGISTER:
-      return "WRITE_JOB_FOR_REGISTER";
-    case CreateJobStatus::WRITE_JOB_FOR_UPDATE:
-      return "WRITE_JOB_FOR_UPDATE";
+    case CreateJobStatus::WRITE_JOB:
+      return "WRITE_JOB";
+    case CreateJobStatus::WRITE_JOB_WITH_INCUMBENT:
+      return "WRITE_JOB_WITH_INCUMBENT";
     case CreateJobStatus::READ_JOB:
       return "READ_JOB";
     case CreateJobStatus::READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT:
       return "READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT";
     case CreateJobStatus::ERROR_NO_PROVIDER:
       return "ERROR_NO_PROVIDER";
-    case CreateJobStatus::ERROR_NO_VERSION:
-      return "ERROR_NO_VERSION";
     case CreateJobStatus::ERROR_REDUNDANT_VERSION:
       return "ERROR_REDUNDANT_VERSION";
     case CreateJobStatus::ERROR_NO_CONTEXT:
@@ -113,7 +111,8 @@
   net::URLRequestJob* job =
       MaybeCreateJobImpl(request, network_delegate, &status);
   const bool is_main_script = resource_type_ == RESOURCE_TYPE_SERVICE_WORKER;
-  // TODO(falken): Add UMA for CreateJobStatus.
+  ServiceWorkerMetrics::RecordContextRequestHandlerStatus(
+      status, IsInstalled(version_.get()), is_main_script);
   if (is_main_script)
     version_->NotifyMainScriptJobCreated(status);
   if (job)
@@ -146,18 +145,15 @@
     net::URLRequest* request,
     net::NetworkDelegate* network_delegate,
     CreateJobStatus* out_status) {
-  if (!provider_host_) {
-    *out_status = CreateJobStatus::ERROR_NO_PROVIDER;
-    return nullptr;
-  }
   if (!context_) {
     *out_status = CreateJobStatus::ERROR_NO_CONTEXT;
     return nullptr;
   }
-  if (!version_) {
-    *out_status = CreateJobStatus::ERROR_NO_VERSION;
+  if (!provider_host_) {
+    *out_status = CreateJobStatus::ERROR_NO_PROVIDER;
     return nullptr;
   }
+
   // This could happen if browser-side has set the status to redundant but the
   // worker has not yet stopped. The worker is already doomed so just reject the
   // request. Handle it specially here because otherwise it'd be unclear whether
@@ -227,15 +223,16 @@
                                              ? registration->waiting_version()
                                              : registration->active_version();
   int64_t incumbent_resource_id = kInvalidServiceWorkerResourceId;
-  if (stored_version && stored_version->script_url() == request->url()) {
-    incumbent_resource_id =
-        stored_version->script_cache_map()->LookupResourceId(request->url());
-  }
-  if (is_main_script)
+  if (is_main_script) {
+    if (stored_version && stored_version->script_url() == request->url()) {
+      incumbent_resource_id =
+          stored_version->script_cache_map()->LookupResourceId(request->url());
+    }
     version_->embedded_worker()->OnURLJobCreatedForMainScript();
+  }
   *out_status = incumbent_resource_id == kInvalidServiceWorkerResourceId
-                    ? CreateJobStatus::WRITE_JOB_FOR_REGISTER
-                    : CreateJobStatus::WRITE_JOB_FOR_UPDATE;
+                    ? CreateJobStatus::WRITE_JOB
+                    : CreateJobStatus::WRITE_JOB_WITH_INCUMBENT;
   return new ServiceWorkerWriteToCacheJob(
       request, network_delegate, resource_type_, context_, version_.get(),
       extra_load_flags, resource_id, incumbent_resource_id);
diff --git a/content/browser/service_worker/service_worker_context_request_handler.h b/content/browser/service_worker/service_worker_context_request_handler.h
index 1f00dabf..26bf3869 100644
--- a/content/browser/service_worker/service_worker_context_request_handler.h
+++ b/content/browser/service_worker/service_worker_context_request_handler.h
@@ -19,22 +19,38 @@
 class CONTENT_EXPORT ServiceWorkerContextRequestHandler
     : public ServiceWorkerRequestHandler {
  public:
+  // The result status for MaybeCreateJob. Used in histograms. Append-only.
   enum class CreateJobStatus {
+    // Unitialized status.
     UNINITIALIZED,
-    WRITE_JOB_FOR_REGISTER,
-    WRITE_JOB_FOR_UPDATE,
+    // A ServiceWorkerWriteToCacheJob was created.
+    WRITE_JOB,
+    // A ServiceWorkerWriteToCacheJob was created with an incumbent script to
+    // compare against.
+    WRITE_JOB_WITH_INCUMBENT,
+    // A ServiceWorkerReadFromCacheJob was created.
     READ_JOB,
-    // When a new worker imports a script that was already imported.
+    // A ServiceWorkerReadFromCacheJob was created for a new worker that is
+    // importing a script that was already imported.
     READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT,
+    // A job could not be created because there is no live
+    // ServiceWorkerProviderHost.
     ERROR_NO_PROVIDER,
-    ERROR_NO_VERSION,
+    // A job could not be created because the ServiceWorkerVersion is in status
+    // REDUNDANT.
     ERROR_REDUNDANT_VERSION,
+    // A job could not be created because there is no live
+    // ServiceWorkerContextCore.
     ERROR_NO_CONTEXT,
+    // A job could not be created because a redirect occurred.
     ERROR_REDIRECT,
-    // When an installed worker imports a script that was not stored at
-    // installation time.
+    // A job was not created because an installed worker is importing a script
+    // that was not stored at installation time.
     ERROR_UNINSTALLED_SCRIPT_IMPORT,
+    // A job could not be created because there are no resource ids available.
     ERROR_OUT_OF_RESOURCE_IDS,
+    // Add new types here.
+
     NUM_TYPES
   };
 
diff --git a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
index 96486459f..c6ed19f 100644
--- a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
 #include "base/run_loop.h"
+#include "base/test/histogram_tester.h"
 #include "content/browser/browser_thread_impl.h"
 #include "content/browser/fileapi/mock_url_request_delegate.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
@@ -30,12 +31,6 @@
 
 namespace content {
 
-namespace {
-
-void EmptyCallback() {}
-
-}  // namespace
-
 class MockHttpProtocolHandler
     : public net::URLRequestJobFactory::ProtocolHandler {
  public:
@@ -62,29 +57,19 @@
 
   void SetUp() override {
     helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath()));
+    context()->storage()->LazyInitialize(base::Bind(&base::DoNothing));
+    base::RunLoop().RunUntilIdle();
 
     // A new unstored registration/version.
     scope_ = GURL("https://host/scope/");
     script_url_ = GURL("https://host/script.js");
+    import_script_url_ = GURL("https://host/import.js");
     registration_ = new ServiceWorkerRegistration(
         scope_, 1L, context()->AsWeakPtr());
-    version_ = new ServiceWorkerVersion(
-        registration_.get(), script_url_, 1L, context()->AsWeakPtr());
-
-    // A provider host for the version.
-    std::unique_ptr<ServiceWorkerProviderHost> host(
-        new ServiceWorkerProviderHost(
-            helper_->mock_render_process_id(),
-            MSG_ROUTING_NONE /* render_frame_id */, 1 /* provider_id */,
-            SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
-            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
-            context()->AsWeakPtr(), nullptr));
-    provider_host_ = host->AsWeakPtr();
-    context()->AddProviderHost(std::move(host));
-    provider_host_->running_hosted_version_ = version_;
-
-    context()->storage()->LazyInitialize(base::Bind(&EmptyCallback));
-    base::RunLoop().RunUntilIdle();
+    version_ = new ServiceWorkerVersion(registration_.get(), script_url_,
+                                        context()->storage()->NewVersionId(),
+                                        context()->AsWeakPtr());
+    SetUpProvider();
 
     std::unique_ptr<MockHttpProtocolHandler> handler(
         new MockHttpProtocolHandler(
@@ -101,11 +86,43 @@
 
   ServiceWorkerContextCore* context() const { return helper_->context(); }
 
-  EmbeddedWorkerTestHelper* helper() { return helper_.get(); }
-  ServiceWorkerVersion* version() { return version_.get(); }
-  ServiceWorkerProviderHost* provider_host() { return provider_host_.get(); }
-  storage::BlobStorageContext* blob_storage_context() {
-    return &blob_storage_context_;
+  void SetUpProvider() {
+    std::unique_ptr<ServiceWorkerProviderHost> host(
+        new ServiceWorkerProviderHost(
+            helper_->mock_render_process_id(),
+            MSG_ROUTING_NONE /* render_frame_id */, 1 /* provider_id */,
+            SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
+            ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
+            context()->AsWeakPtr(), nullptr));
+    provider_host_ = host->AsWeakPtr();
+    context()->AddProviderHost(std::move(host));
+    provider_host_->running_hosted_version_ = version_;
+  }
+
+  std::unique_ptr<net::URLRequest> CreateRequest(const GURL& url) {
+    return url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY,
+                                              &url_request_delegate_);
+  }
+
+  // Creates a ServiceWorkerContextHandler directly.
+  std::unique_ptr<ServiceWorkerContextRequestHandler> CreateHandler(
+      ResourceType resource_type) {
+    return base::MakeUnique<ServiceWorkerContextRequestHandler>(
+        context()->AsWeakPtr(), provider_host_,
+        base::WeakPtr<storage::BlobStorageContext>(), resource_type);
+  }
+
+  // Associates a ServiceWorkerRequestHandler with a request. Use this instead
+  // of CreateHandler if you want to actually start the request to test what the
+  // job created by the handler does.
+  void InitializeHandler(net::URLRequest* request) {
+    ServiceWorkerRequestHandler::InitializeHandler(
+        request, helper_->context_wrapper(), &blob_storage_context_,
+        helper_->mock_render_process_id(), provider_host_->provider_id(),
+        false /* skip_service_worker */, FETCH_REQUEST_MODE_NO_CORS,
+        FETCH_CREDENTIALS_MODE_OMIT, FetchRedirectMode::FOLLOW_MODE,
+        RESOURCE_TYPE_SERVICE_WORKER, REQUEST_CONTEXT_TYPE_SERVICE_WORKER,
+        REQUEST_CONTEXT_FRAME_TYPE_NONE, nullptr);
   }
 
  protected:
@@ -119,6 +136,7 @@
   net::URLRequestJobFactoryImpl url_request_job_factory_;
   GURL scope_;
   GURL script_url_;
+  GURL import_script_url_;
   storage::BlobStorageContext blob_storage_context_;
 };
 
@@ -129,19 +147,20 @@
   version_->SetStatus(ServiceWorkerVersion::NEW);
 
   // Conduct a resource fetch for the main script.
-  const GURL kScriptUrl("https://host/script.js");
-  std::unique_ptr<net::URLRequest> request = url_request_context_.CreateRequest(
-      kScriptUrl, net::DEFAULT_PRIORITY, &url_request_delegate_);
+  base::HistogramTester histograms;
+  std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
   std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
-      new ServiceWorkerContextRequestHandler(
-          context()->AsWeakPtr(), provider_host_,
-          base::WeakPtr<storage::BlobStorageContext>(),
-          RESOURCE_TYPE_SERVICE_WORKER));
+      CreateHandler(RESOURCE_TYPE_SERVICE_WORKER));
   std::unique_ptr<net::URLRequestJob> job(
       handler->MaybeCreateJob(request.get(), nullptr, nullptr));
   ASSERT_TRUE(job.get());
   ServiceWorkerWriteToCacheJob* sw_job =
       static_cast<ServiceWorkerWriteToCacheJob*>(job.get());
+  histograms.ExpectUniqueSample(
+      "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+      static_cast<int>(
+          ServiceWorkerContextRequestHandler::CreateJobStatus::WRITE_JOB),
+      1);
 
   // Verify the net request is not initialized to bypass the browser cache.
   EXPECT_FALSE(sw_job->net_request_->load_flags() & net::LOAD_BYPASS_CACHE);
@@ -155,19 +174,20 @@
   version_->SetStatus(ServiceWorkerVersion::NEW);
 
   // Conduct a resource fetch for the main script.
-  const GURL kScriptUrl("https://host/script.js");
-  std::unique_ptr<net::URLRequest> request = url_request_context_.CreateRequest(
-      kScriptUrl, net::DEFAULT_PRIORITY, &url_request_delegate_);
+  base::HistogramTester histograms;
+  std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
   std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
-      new ServiceWorkerContextRequestHandler(
-          context()->AsWeakPtr(), provider_host_,
-          base::WeakPtr<storage::BlobStorageContext>(),
-          RESOURCE_TYPE_SERVICE_WORKER));
+      CreateHandler(RESOURCE_TYPE_SERVICE_WORKER));
   std::unique_ptr<net::URLRequestJob> job(
       handler->MaybeCreateJob(request.get(), nullptr, nullptr));
   ASSERT_TRUE(job.get());
   ServiceWorkerWriteToCacheJob* sw_job =
       static_cast<ServiceWorkerWriteToCacheJob*>(job.get());
+  histograms.ExpectUniqueSample(
+      "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+      static_cast<int>(
+          ServiceWorkerContextRequestHandler::CreateJobStatus::WRITE_JOB),
+      1);
 
   // Verify the net request is initialized to bypass the browser cache.
   EXPECT_TRUE(sw_job->net_request_->load_flags() & net::LOAD_BYPASS_CACHE);
@@ -181,14 +201,9 @@
   version_->set_force_bypass_cache_for_scripts(true);
 
   // Conduct a resource fetch for the main script.
-  const GURL kScriptUrl("https://host/script.js");
-  std::unique_ptr<net::URLRequest> request = url_request_context_.CreateRequest(
-      kScriptUrl, net::DEFAULT_PRIORITY, &url_request_delegate_);
+  std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
   std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
-      new ServiceWorkerContextRequestHandler(
-          context()->AsWeakPtr(), provider_host_,
-          base::WeakPtr<storage::BlobStorageContext>(),
-          RESOURCE_TYPE_SERVICE_WORKER));
+      CreateHandler(RESOURCE_TYPE_SERVICE_WORKER));
   std::unique_ptr<net::URLRequestJob> job(
       handler->MaybeCreateJob(request.get(), nullptr, nullptr));
   ASSERT_TRUE(job.get());
@@ -204,20 +219,21 @@
   version_->SetStatus(ServiceWorkerVersion::NEW);
 
   // Conduct a resource fetch for the main script.
-  const GURL kScriptUrl("https://host/script.js");
-  std::unique_ptr<net::URLRequest> request = url_request_context_.CreateRequest(
-      kScriptUrl, net::DEFAULT_PRIORITY, &url_request_delegate_);
+  base::HistogramTester histograms;
+  std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
   std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
-      new ServiceWorkerContextRequestHandler(
-          context()->AsWeakPtr(), provider_host_,
-          base::WeakPtr<storage::BlobStorageContext>(),
-          RESOURCE_TYPE_SERVICE_WORKER));
+      CreateHandler(RESOURCE_TYPE_SERVICE_WORKER));
   std::unique_ptr<net::URLRequestJob> job(
       handler->MaybeCreateJob(request.get(), nullptr, nullptr));
   ASSERT_TRUE(job.get());
   ServiceWorkerWriteToCacheJob* sw_job =
       static_cast<ServiceWorkerWriteToCacheJob*>(job.get());
 
+  histograms.ExpectUniqueSample(
+      "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+      static_cast<int>(
+          ServiceWorkerContextRequestHandler::CreateJobStatus::WRITE_JOB),
+      1);
   // Verify that the request is properly annotated as originating from a
   // Service Worker.
   EXPECT_TRUE(ResourceRequestInfo::OriginatedFromServiceWorker(
@@ -230,9 +246,7 @@
        SkipServiceWorkerForServiceWorkerRequest) {
   // Conduct a resource fetch for the main script.
   version_->SetStatus(ServiceWorkerVersion::NEW);
-  const GURL kScriptUrl("https://host/script.js");
-  std::unique_ptr<net::URLRequest> request = url_request_context_.CreateRequest(
-      kScriptUrl, net::DEFAULT_PRIORITY, &url_request_delegate_);
+  std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
   ServiceWorkerRequestHandler::InitializeHandler(
       request.get(), helper_->context_wrapper(), &blob_storage_context_,
       helper_->mock_render_process_id(), provider_host_->provider_id(),
@@ -246,25 +260,182 @@
   EXPECT_TRUE(handler);
 }
 
-TEST_F(ServiceWorkerContextRequestHandlerTest, RedundantVersion) {
-  // Create a redundant version.
-  version_->SetStatus(ServiceWorkerVersion::REDUNDANT);
+TEST_F(ServiceWorkerContextRequestHandlerTest, NewWorker) {
+  // Conduct a resource fetch for the main script.
+  {
+    base::HistogramTester histograms;
+    std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
+    std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
+        CreateHandler(RESOURCE_TYPE_SERVICE_WORKER));
+    std::unique_ptr<net::URLRequestJob> job(
+        handler->MaybeCreateJob(request.get(), nullptr, nullptr));
+    EXPECT_TRUE(job);
+    histograms.ExpectUniqueSample(
+        "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+        static_cast<int>(
+            ServiceWorkerContextRequestHandler::CreateJobStatus::WRITE_JOB),
+        1);
+  }
+
+  // Conduct a resource fetch for an imported script.
+  {
+    base::HistogramTester histograms;
+    std::unique_ptr<net::URLRequest> request(CreateRequest(import_script_url_));
+    std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
+        CreateHandler(RESOURCE_TYPE_SCRIPT));
+    std::unique_ptr<net::URLRequestJob> job(
+        handler->MaybeCreateJob(request.get(), nullptr, nullptr));
+    EXPECT_TRUE(job);
+    histograms.ExpectUniqueSample(
+        "ServiceWorker.ContextRequestHandlerStatus.NewWorker.ImportedScript",
+        static_cast<int>(
+            ServiceWorkerContextRequestHandler::CreateJobStatus::WRITE_JOB),
+        1);
+  }
+}
+
+TEST_F(ServiceWorkerContextRequestHandlerTest, InstalledWorker) {
+  using Resource = ServiceWorkerDatabase::ResourceRecord;
+  std::vector<Resource> resources = {
+      Resource(context()->storage()->NewResourceId(), script_url_, 100),
+      Resource(context()->storage()->NewResourceId(), import_script_url_, 100)};
+  version_->script_cache_map()->SetResources(resources);
+  version_->set_fetch_handler_existence(
+      ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
+  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+  registration_->SetActiveVersion(version_);
 
   // Conduct a resource fetch for the main script.
-  std::unique_ptr<net::URLRequest> request = url_request_context_.CreateRequest(
-      version_->script_url(), net::DEFAULT_PRIORITY, &url_request_delegate_);
-  ServiceWorkerRequestHandler::InitializeHandler(
-      request.get(), helper()->context_wrapper(), blob_storage_context(),
-      helper()->mock_render_process_id(), provider_host()->provider_id(),
-      true /* skip_service_worker */, FETCH_REQUEST_MODE_NO_CORS,
-      FETCH_CREDENTIALS_MODE_OMIT, FetchRedirectMode::FOLLOW_MODE,
-      RESOURCE_TYPE_SERVICE_WORKER, REQUEST_CONTEXT_TYPE_SERVICE_WORKER,
-      REQUEST_CONTEXT_FRAME_TYPE_NONE, nullptr);
+  {
+    base::HistogramTester histograms;
+    std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
+    std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
+        CreateHandler(RESOURCE_TYPE_SERVICE_WORKER));
+    std::unique_ptr<net::URLRequestJob> job(
+        handler->MaybeCreateJob(request.get(), nullptr, nullptr));
+    EXPECT_TRUE(job);
+    histograms.ExpectUniqueSample(
+        "ServiceWorker.ContextRequestHandlerStatus.InstalledWorker.MainScript",
+        static_cast<int>(
+            ServiceWorkerContextRequestHandler::CreateJobStatus::READ_JOB),
+        1);
+  }
 
-  // Verify that the request fails.
-  request->Start();
-  base::RunLoop().Run();
-  EXPECT_EQ(net::ERR_FAILED, url_request_delegate_.request_status());
+  // Conduct a resource fetch for an imported script.
+  {
+    base::HistogramTester histograms;
+    std::unique_ptr<net::URLRequest> request(CreateRequest(import_script_url_));
+    std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
+        CreateHandler(RESOURCE_TYPE_SCRIPT));
+    std::unique_ptr<net::URLRequestJob> job(
+        handler->MaybeCreateJob(request.get(), nullptr, nullptr));
+    EXPECT_TRUE(job);
+    histograms.ExpectUniqueSample(
+        "ServiceWorker.ContextRequestHandlerStatus.InstalledWorker."
+        "ImportedScript",
+        static_cast<int>(
+            ServiceWorkerContextRequestHandler::CreateJobStatus::READ_JOB),
+        1);
+  }
+}
+
+TEST_F(ServiceWorkerContextRequestHandlerTest, Incumbent) {
+  // Make an incumbent version.
+  scoped_refptr<ServiceWorkerVersion> incumbent = new ServiceWorkerVersion(
+      registration_.get(), script_url_, context()->storage()->NewVersionId(),
+      context()->AsWeakPtr());
+  incumbent->set_fetch_handler_existence(
+      ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
+  std::vector<ServiceWorkerDatabase::ResourceRecord> resources = {
+      ServiceWorkerDatabase::ResourceRecord(
+          context()->storage()->NewResourceId(), script_url_, 100)};
+  incumbent->script_cache_map()->SetResources(resources);
+  incumbent->SetStatus(ServiceWorkerVersion::ACTIVATED);
+  registration_->SetActiveVersion(incumbent);
+
+  // Make a new version.
+  version_->SetStatus(ServiceWorkerVersion::NEW);
+
+  // Conduct a resource fetch for the main script.
+  base::HistogramTester histograms;
+  std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
+  std::unique_ptr<ServiceWorkerContextRequestHandler> handler(
+      CreateHandler(RESOURCE_TYPE_SERVICE_WORKER));
+  std::unique_ptr<net::URLRequestJob> job(
+      handler->MaybeCreateJob(request.get(), nullptr, nullptr));
+  histograms.ExpectUniqueSample(
+      "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+      static_cast<int>(ServiceWorkerContextRequestHandler::CreateJobStatus::
+                           WRITE_JOB_WITH_INCUMBENT),
+      1);
+}
+
+TEST_F(ServiceWorkerContextRequestHandlerTest, ErrorCases) {
+  {
+    // Set up a request.
+    base::HistogramTester histograms;
+    std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
+    InitializeHandler(request.get());
+
+    // Make the version redundant.
+    version_->SetStatus(ServiceWorkerVersion::REDUNDANT);
+
+    // Verify that the request fails.
+    request->Start();
+    base::RunLoop().Run();
+    EXPECT_EQ(net::ERR_FAILED, url_request_delegate_.request_status());
+    histograms.ExpectUniqueSample(
+        "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+        static_cast<int>(ServiceWorkerContextRequestHandler::CreateJobStatus::
+                             ERROR_REDUNDANT_VERSION),
+        1);
+  }
+  // Return the version to normal.
+  version_->SetStatus(ServiceWorkerVersion::NEW);
+
+  {
+    // Set up a request.
+    base::HistogramTester histograms;
+    std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
+    InitializeHandler(request.get());
+
+    // Remove the host.
+    context()->RemoveProviderHost(helper_->mock_render_process_id(),
+                                  provider_host_->provider_id());
+
+    // Verify that the request fails.
+    request->Start();
+    base::RunLoop().Run();
+    EXPECT_EQ(net::ERR_FAILED, url_request_delegate_.request_status());
+    histograms.ExpectUniqueSample(
+        "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+        static_cast<int>(ServiceWorkerContextRequestHandler::CreateJobStatus::
+                             ERROR_NO_PROVIDER),
+        1);
+  }
+  // Recreate the host.
+  SetUpProvider();
+
+  {
+    // Set up a request.
+    base::HistogramTester histograms;
+    std::unique_ptr<net::URLRequest> request(CreateRequest(script_url_));
+    InitializeHandler(request.get());
+
+    // Destroy the context.
+    helper_->ShutdownContext();
+    base::RunLoop().RunUntilIdle();
+
+    // Verify that the request fails.
+    request->Start();
+    base::RunLoop().Run();
+    EXPECT_EQ(net::ERR_FAILED, url_request_delegate_.request_status());
+    histograms.ExpectUniqueSample(
+        "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+        static_cast<int>(ServiceWorkerContextRequestHandler::CreateJobStatus::
+                             ERROR_NO_CONTEXT),
+        1);
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_metrics.cc b/content/browser/service_worker/service_worker_metrics.cc
index d7c7533b..af9dcb1 100644
--- a/content/browser/service_worker/service_worker_metrics.cc
+++ b/content/browser/service_worker/service_worker_metrics.cc
@@ -767,4 +767,37 @@
                               size);
 }
 
+void ServiceWorkerMetrics::RecordContextRequestHandlerStatus(
+    ServiceWorkerContextRequestHandler::CreateJobStatus status,
+    bool is_installed,
+    bool is_main_script) {
+  const int value = static_cast<int>(status);
+  const int max = static_cast<int>(
+      ServiceWorkerContextRequestHandler::CreateJobStatus::NUM_TYPES);
+  if (is_installed) {
+    if (is_main_script) {
+      UMA_HISTOGRAM_ENUMERATION(
+          "ServiceWorker.ContextRequestHandlerStatus.InstalledWorker."
+          "MainScript",
+          value, max);
+    } else {
+      UMA_HISTOGRAM_ENUMERATION(
+          "ServiceWorker.ContextRequestHandlerStatus.InstalledWorker."
+          "ImportedScript",
+          value, max);
+    }
+  } else {
+    if (is_main_script) {
+      UMA_HISTOGRAM_ENUMERATION(
+          "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript",
+          value, max);
+    } else {
+      UMA_HISTOGRAM_ENUMERATION(
+          "ServiceWorker.ContextRequestHandlerStatus.NewWorker."
+          "ImportedScript",
+          value, max);
+    }
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_metrics.h b/content/browser/service_worker/service_worker_metrics.h
index 9b75bd3f..40b340d 100644
--- a/content/browser/service_worker/service_worker_metrics.h
+++ b/content/browser/service_worker/service_worker_metrics.h
@@ -290,6 +290,13 @@
   // navigation preload request is to be sent.
   static void RecordNavigationPreloadRequestHeaderSize(size_t size);
 
+  // Records the result of trying to handle a request for a service worker
+  // script.
+  static void RecordContextRequestHandlerStatus(
+      ServiceWorkerContextRequestHandler::CreateJobStatus status,
+      bool is_installed,
+      bool is_main_script);
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceWorkerMetrics);
 };
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
index 8b919655..f151874 100644
--- a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
+++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
@@ -282,18 +282,39 @@
   DCHECK(entry->DistilledState() == ReadingListEntry::PROCESSED);
   GURL url =
       reading_list::DistilledURLForPath(entry->DistilledPath(), entry->URL());
-  web::NavigationManager* manager = web_state()->GetNavigationManager();
-  web::NavigationItem* item = manager->GetPendingItem();
-  if (item) {
+  web::NavigationManager* navigationManager =
+      web_state()->GetNavigationManager();
+  web::NavigationItem* item = navigationManager->GetPendingItem();
+  if (!item) {
+    // Either the loading finished on error and the item is already committed,
+    // or the page is being reloaded and due to crbug.com/676129. there is no
+    // pending item. Either way, the correct item to reuse is the last committed
+    // item.
+    // TODO(crbug.com/676129): this case can be removed.
+    item = navigationManager->GetLastCommittedItem();
+    item->SetURL(url);
+    item->SetVirtualURL(pending_url_);
+    navigationManager->Reload(false);
+  } else if (navigationManager->GetPendingItemIndex() != -1 &&
+             navigationManager->GetItemAtIndex(
+                 navigationManager->GetPendingItemIndex()) == item) {
+    // The navigation corresponds to a back/forward. The item on the stack can
+    // be reused for the offline navigation.
+    // TODO(crbug.com/665189): GetPendingItemIndex() will return currentEntry()
+    // if navigating to a new URL. Test the addresses to verify that
+    // GetPendingItemIndex() actually returns the pending item index. Remove
+    // this extra test on item addresses once bug 665189 is fixed.
+    item->SetURL(url);
+    item->SetVirtualURL(pending_url_);
+    navigationManager->GoToIndex(navigationManager->GetPendingItemIndex());
+  } else {
+    // The pending item corresponds to a new navigation and will be discarded
+    // on next navigation.
+    // Trigger a new navigation on the offline URL.
     web::WebState::OpenURLParams params(url, item->GetReferrer(),
                                         WindowOpenDisposition::CURRENT_TAB,
                                         item->GetTransitionType(), NO);
     web_state()->OpenURL(params);
-  } else {
-    item = manager->GetLastCommittedItem();
-    item->SetURL(url);
-    item->SetVirtualURL(pending_url_);
-    web_state()->GetNavigationManager()->Reload(false);
   }
   reading_list_model_->SetReadStatus(entry->URL(), true);
   UMA_HISTOGRAM_BOOLEAN("ReadingList.OfflineVersionDisplayed", true);
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer_unittest.mm b/ios/chrome/browser/reading_list/reading_list_web_state_observer_unittest.mm
index 4f30e28..43d8f04f 100644
--- a/ios/chrome/browser/reading_list/reading_list_web_state_observer_unittest.mm
+++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer_unittest.mm
@@ -120,13 +120,18 @@
   GURL distilled_url =
       reading_list::DistilledURLForPath(entry->DistilledPath(), entry->URL());
 
-  test_navigation_manager_->GetPendingItem()->SetURL(url);
+  // Test on commited entry, there must be no pending item.
+  test_navigation_manager_->SetPendingItem(nullptr);
+  test_navigation_manager_->GetLastCommittedItem()->SetURL(url);
   test_web_state_.SetLoading(true);
   test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::FAILURE);
   test_web_state_.SetLoading(false);
 
-  EXPECT_FALSE(test_navigation_manager_->ReloadCalled());
-  EXPECT_EQ(test_web_state_.LastOpenedUrl(), distilled_url);
+  EXPECT_TRUE(test_navigation_manager_->ReloadCalled());
+  EXPECT_EQ(test_navigation_manager_->GetLastCommittedItem()->GetVirtualURL(),
+            url);
+  EXPECT_EQ(test_navigation_manager_->GetLastCommittedItem()->GetURL(),
+            distilled_url);
   EXPECT_TRUE(entry->IsRead());
 }
 
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index b768c1e4..6749fc2 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -2455,20 +2455,6 @@
     if (web::UrlHasWebScheme(link)) {
       web::Referrer referrer([_model currentTab].url, params.referrer_policy);
 
-      if (reading_list::switches::IsReadingListEnabled()) {
-        NSString* innerText = params.link_text;
-        if ([innerText length] > 0) {
-          // Add to reading list.
-          title = l10n_util::GetNSStringWithFixup(
-              IDS_IOS_CONTENT_CONTEXT_ADDTOREADINGLIST);
-          action = ^{
-            Record(ACTION_READ_LATER, isImage, isLink);
-            [weakSelf addToReadingListURL:link title:innerText];
-          };
-          [_contextMenuCoordinator addItemWithTitle:title action:action];
-        }
-      }
-
       // Open in New Tab.
       title = l10n_util::GetNSStringWithFixup(
           IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB);
@@ -2496,6 +2482,20 @@
         };
         [_contextMenuCoordinator addItemWithTitle:title action:action];
       }
+
+      if (reading_list::switches::IsReadingListEnabled()) {
+        NSString* innerText = params.link_text;
+        if ([innerText length] > 0) {
+          // Add to reading list.
+          title = l10n_util::GetNSStringWithFixup(
+              IDS_IOS_CONTENT_CONTEXT_ADDTOREADINGLIST);
+          action = ^{
+            Record(ACTION_READ_LATER, isImage, isLink);
+            [weakSelf addToReadingListURL:link title:innerText];
+          };
+          [_contextMenuCoordinator addItemWithTitle:title action:action];
+        }
+      }
     }
     // Copy Link.
     title = l10n_util::GetNSStringWithFixup(IDS_IOS_CONTENT_CONTEXT_COPY);
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
index 6d78d91..42ebcf2 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_view_controller.mm
@@ -151,12 +151,16 @@
 - (void)initializeActionSheet;
 // Exits the editing mode and update the toolbar state with animation.
 - (void)exitEditingModeAnimated:(BOOL)animated;
-// Applies |action| to every cell in the section |identifier|.
+// Applies |updater| to the URL of every cell in the section |identifier|. The
+// updates are done in reverse order of the cells in the section to keep the
+// order. The monitoring of the model updates are suspended during this time.
 - (void)updateItemsInSectionIdentifier:(SectionIdentifier)identifier
                      usingEntryUpdater:(EntryUpdater)updater;
-// Applies |action| to every selected element of collection view. The monitoring
-// of the model updates is stopped during this time.
-- (void)updateSelectedItemsWithEntryUpdater:(EntryUpdater)updater;
+// Applies |updater| to the URL of every element in |indexPaths|. The updates
+// are done in reverse order |indexPaths| to keep the order. The monitoring of
+// the model updates are suspended during this time.
+- (void)updateIndexPaths:(NSArray<NSIndexPath*>*)indexPaths
+       usingEntryUpdater:(EntryUpdater)updater;
 // Logs the deletions histograms for the entry with |url|.
 - (void)logDeletionHistogramsForEntry:(const GURL&)url;
 // Move all the items from |sourceSectionIdentifier| to
@@ -810,24 +814,26 @@
 
 - (void)markItemsRead {
   base::RecordAction(base::UserMetricsAction("MobileReadingListMarkRead"));
-  [self updateSelectedItemsWithEntryUpdater:^(const GURL& url) {
-    [self readingListModel]->SetReadStatus(url, true);
-  }];
-
   NSArray* sortedIndexPaths = [self.collectionView.indexPathsForSelectedItems
       sortedArrayUsingSelector:@selector(compare:)];
+  [self updateIndexPaths:sortedIndexPaths
+       usingEntryUpdater:^(const GURL& url) {
+         [self readingListModel]->SetReadStatus(url, true);
+       }];
+
   [self exitEditingModeAnimated:YES];
   [self moveSelectedItems:sortedIndexPaths toSection:SectionIdentifierRead];
 }
 
 - (void)markItemsUnread {
   base::RecordAction(base::UserMetricsAction("MobileReadingListMarkUnread"));
-  [self updateSelectedItemsWithEntryUpdater:^(const GURL& url) {
-    [self readingListModel]->SetReadStatus(url, false);
-  }];
-
   NSArray* sortedIndexPaths = [self.collectionView.indexPathsForSelectedItems
       sortedArrayUsingSelector:@selector(compare:)];
+  [self updateIndexPaths:sortedIndexPaths
+       usingEntryUpdater:^(const GURL& url) {
+         [self readingListModel]->SetReadStatus(url, false);
+       }];
+
   [self exitEditingModeAnimated:YES];
   [self moveSelectedItems:sortedIndexPaths toSection:SectionIdentifierUnread];
 }
@@ -864,12 +870,13 @@
 }
 
 - (void)deleteSelectedItems {
-  [self updateSelectedItemsWithEntryUpdater:^(const GURL& url) {
-    [self logDeletionHistogramsForEntry:url];
-    [self readingListModel]->RemoveEntryByURL(url);
-  }];
-
   NSArray* indexPaths = [self.collectionView.indexPathsForSelectedItems copy];
+  [self updateIndexPaths:indexPaths
+       usingEntryUpdater:^(const GURL& url) {
+         [self logDeletionHistogramsForEntry:url];
+         [self readingListModel]->RemoveEntryByURL(url);
+       }];
+
   [self exitEditingModeAnimated:YES];
 
   [self.collectionView performBatchUpdates:^{
@@ -889,21 +896,27 @@
 
 - (void)updateItemsInSectionIdentifier:(SectionIdentifier)identifier
                      usingEntryUpdater:(EntryUpdater)updater {
+  _shouldMonitorModel = NO;
   auto token = self.readingListModel->BeginBatchUpdates();
   NSArray* readItems =
       [self.collectionViewModel itemsInSectionWithIdentifier:identifier];
-  for (id item in readItems) {
+  // Read the objects in reverse order to keep the order (last modified first).
+  for (id item in [readItems reverseObjectEnumerator]) {
     ReadingListCollectionViewItem* readingListItem =
         base::mac::ObjCCastStrict<ReadingListCollectionViewItem>(item);
     if (updater)
       updater(readingListItem.url);
   }
+  token.reset();
+  _shouldMonitorModel = YES;
 }
 
-- (void)updateSelectedItemsWithEntryUpdater:(EntryUpdater)updater {
+- (void)updateIndexPaths:(NSArray<NSIndexPath*>*)indexPaths
+       usingEntryUpdater:(EntryUpdater)updater {
   _shouldMonitorModel = NO;
   auto token = self.readingListModel->BeginBatchUpdates();
-  for (NSIndexPath* index in self.collectionView.indexPathsForSelectedItems) {
+  // Read the objects in reverse order to keep the order (last modified first).
+  for (NSIndexPath* index in [indexPaths reverseObjectEnumerator]) {
     CollectionViewItem* cell = [self.collectionViewModel itemAtIndexPath:index];
     ReadingListCollectionViewItem* readingListItem =
         base::mac::ObjCCastStrict<ReadingListCollectionViewItem>(cell);
@@ -988,9 +1001,7 @@
         sectionForSectionIdentifier:sectionIdentifier];
 
     NSInteger newItemIndex = 0;
-    // In order to make sure the we do not end modifying the wrong item, we have
-    // to take the items in the reverse order of the indexPaths.
-    for (NSIndexPath* index in [sortedIndexPaths reverseObjectEnumerator]) {
+    for (NSIndexPath* index in sortedIndexPaths) {
       // The |sortedIndexPaths| is a copy of the index paths before the
       // destination section has been added if necessary. The section part of
       // the index potentially needs to be updated.
@@ -1005,13 +1016,16 @@
 
       NSIndexPath* updatedIndex =
           [NSIndexPath indexPathForItem:index.item inSection:updatedSection];
+      NSIndexPath* indexForModel =
+          [NSIndexPath indexPathForItem:index.item - newItemIndex
+                              inSection:updatedSection];
 
       // Index of the item in the new section. The newItemIndex is the index of
       // this item in the targeted section.
       NSIndexPath* newIndexPath =
           [NSIndexPath indexPathForItem:newItemIndex++ inSection:section];
       [self collectionView:self.collectionView
-          willMoveItemAtIndexPath:updatedIndex
+          willMoveItemAtIndexPath:indexForModel
                       toIndexPath:newIndexPath];
       [self.collectionView moveItemAtIndexPath:updatedIndex
                                    toIndexPath:newIndexPath];
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h
index 7dbd0cf..ca03cc62 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h
@@ -44,6 +44,7 @@
 @property(nonatomic, readonly) UIView* topBar;
 
 // Sets the cell's appearance using information in |tab|.
+// The delegate needs to be set before calling this method.
 - (void)setAppearanceForTab:(Tab*)tab cellSize:(CGSize)cellSize;
 // Sets the cell's appearance depending on |type|.
 - (void)setSessionType:(TabSwitcherSessionType)type;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm
index 1bd0b375..bad9ca2 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm
@@ -230,6 +230,8 @@
   CGSize snapshotSize = cellSize;
   snapshotSize.height -= tabSwitcherLocalSessionCellTopBarHeight();
   base::WeakNSObject<TabSwitcherLocalSessionCell> weakCell(self);
+  DCHECK(self.delegate);
+  DCHECK([self cache]);
   _currentPendingSnapshotRequest =
       [[self cache] requestSnapshotForTab:tab
                                  withSize:snapshotSize
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_controller.mm
index aac14c8..da2519be 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_controller.mm
@@ -237,6 +237,7 @@
     [panelCell setTitle:SysUTF16ToNSString(tab->title)];
     [panelCell setSessionGURL:tab->virtual_url
              withBrowserState:[_model browserState]];
+    [panelCell setDelegate:self];
   } else {
     NSString* identifier = [TabSwitcherLocalSessionCell identifier];
     TabSwitcherLocalSessionCell* panelCell =
@@ -247,10 +248,10 @@
 
     Tab* tab = _localSession->tabs()[tabIndex];
     [panelCell setSessionType:_sessionType];
+    [panelCell setDelegate:self];
     [panelCell setAppearanceForTab:tab cellSize:[_panelView cellSize]];
   }
   DCHECK(cell);
-  cell.delegate = self;
   return cell;
 }
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5d3b758..30a468b 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1217,24 +1217,6 @@
             ]
         }
     ],
-    "NewAudioRenderingMixingStrategy": [
-        {
-            "platforms": [
-                "android",
-                "linux",
-                "mac",
-                "win"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "NewAudioRenderingMixingStrategy"
-                    ]
-                }
-            ]
-        }
-    ],
     "NewProfileManagement": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations
index ac42e3d2..49c6e30 100644
--- a/third_party/WebKit/LayoutTests/W3CImportExpectations
+++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -236,8 +236,7 @@
 # imported/wpt/domxpath [ Pass ]
 imported/wpt/dpub-aam [ Skip ]
 imported/wpt/dpub-aria [ Skip ]
-## Owners: dom-dev@chromium.org
-# imported/wpt/editing [ Pass ]
+imported/wpt/editing [ Skip ]
 ## Owners: jsbell@chromium.org
 # imported/wpt/encoding [ Pass ]
 ## Owners: jrummell@chromium.org
@@ -309,8 +308,7 @@
 imported/wpt/resources/webidl2/test [ Skip ]
 imported/wpt/screen-orientation [ Skip ]
 imported/wpt/secure-contexts [ Skip ]
-## Owners: dom-dev@chromium.org
-# imported/wpt/selection [ Pass ]
+imported/wpt/selection [ Skip ]
 imported/wpt/selectors [ Skip ]
 imported/wpt/selectors-api [ Skip ]
 imported/wpt/serve [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/fast/forms/radio/radio-input-keyboard-navigation.html b/third_party/WebKit/LayoutTests/fast/forms/radio/radio-input-keyboard-navigation.html
new file mode 100644
index 0000000..40b9786
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/forms/radio/radio-input-keyboard-navigation.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<style>
+	#test1 {
+		max-height: 65px;
+		overflow: auto;
+		width: 100px;
+		height: 65px;
+	}
+	input[type="radio"] {
+		display: block;
+	}
+</style>
+</head>
+<body>
+<div id="test1">
+	<input type="radio" name="foo" checked>
+	<input type="radio" name="foo">
+	<input type="radio" name="foo">
+	<input type="radio" name="foo">
+	<input type="radio" name="foo">
+	<input type="radio" name="foo">
+</div>
+<script>
+	test(function() {
+		document.querySelector("input").focus();
+		assert_exists(window, "eventSender");
+		for (var i = 0; i < 5; i++) {
+			window.eventSender.keyDown("ArrowDown");
+		}
+		assert_greater_than(document.getElementById("test1").scrollTop, 0);
+	}, "Parent container should be scrolled while navigating through radio inputs");
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/span/empty-block-between-spanners.html b/third_party/WebKit/LayoutTests/fast/multicol/span/empty-block-between-spanners.html
new file mode 100644
index 0000000..475ad27
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/span/empty-block-between-spanners.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<p>PASS if no crash or assertion failure.</p>
+<div style="columns:2; line-height:20px;">
+    <div style="margin-bottom:1px;">
+        <br>
+        <div style="column-span:all;"></div>
+        <div></div>
+        <div style="column-span:all;"></div>
+    </div>
+    <br>
+</div>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+    test(function() { }, "No crash!");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/span/empty-block-with-bottom-margin-between-spanners.html b/third_party/WebKit/LayoutTests/fast/multicol/span/empty-block-with-bottom-margin-between-spanners.html
new file mode 100644
index 0000000..7a3daddd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/span/empty-block-with-bottom-margin-between-spanners.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<p>PASS if no crash or assertion failure.</p>
+<div style="columns:2; line-height:20px;">
+    <br>
+    <div style="column-span:all;"></div>
+    <div style="margin-bottom:1px;">
+        <div style="column-span:all;"></div>
+    </div>
+    <br>
+</div>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+    test(function() { }, "No crash!");
+</script>
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index 7000b35e..ec0777d 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -354,6 +354,7 @@
     "properties/CSSPropertyAPIOutlineOffset.cpp",
     "properties/CSSPropertyAPIPage.cpp",
     "properties/CSSPropertyAPIPaintOrder.cpp",
+    "properties/CSSPropertyAPIQuotes.cpp",
     "properties/CSSPropertyAPIRotate.cpp",
     "properties/CSSPropertyAPIScrollSnapCoordinate.cpp",
     "properties/CSSPropertyAPIShapeMargin.cpp",
@@ -370,6 +371,7 @@
     "properties/CSSPropertyAPIWebkitHighlight.cpp",
     "properties/CSSPropertyAPIWebkitLineClamp.cpp",
     "properties/CSSPropertyAPIWebkitPadding.cpp",
+    "properties/CSSPropertyAPIWebkitTextEmphasisStyle.cpp",
     "properties/CSSPropertyAPIWebkitTransformOriginZ.cpp",
     "properties/CSSPropertyAPIWillChange.cpp",
     "properties/CSSPropertyAPIZIndex.cpp",
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.in b/third_party/WebKit/Source/core/css/CSSProperties.in
index 27001cd..9e7133c 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.in
+++ b/third_party/WebKit/Source/core/css/CSSProperties.in
@@ -342,7 +342,7 @@
 perspective-origin interpolable, converter=convertPosition
 pointer-events inherited, independent, keyword_only, keywords=[none|auto|stroke|fill|painted|visible|visibleStroke|visibleFill|visiblePainted|bounding-box|all], initial_keyword=auto
 position custom_inherit
-quotes inherited, converter=convertQuotes
+quotes inherited, converter=convertQuotes, api_class
 resize custom_value
 right typedom_types=[Length], keywords=[auto], supports_percentage, interpolable, initial=initialOffset, converter=convertLengthOrAuto
 r interpolable, svg, converter=convertLength
@@ -459,7 +459,7 @@
 -webkit-text-combine inherited, type_name=TextCombine, name_for_methods=TextCombine
 -webkit-text-emphasis-color inherited, custom_all
 -webkit-text-emphasis-position inherited, type_name=TextEmphasisPosition
--webkit-text-emphasis-style inherited, custom_all
+-webkit-text-emphasis-style inherited, custom_all, api_class
 -webkit-text-fill-color inherited, custom_all
 -webkit-text-security inherited
 -webkit-text-stroke-color interpolable, inherited, custom_all
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index 86ad465..1989f2025 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -329,19 +329,10 @@
   return settings;
 }
 
-static CSSValue* consumeQuotes(CSSParserTokenRange& range) {
+static CSSValue* consumeWebkitHighlight(CSSParserTokenRange& range) {
   if (range.peek().id() == CSSValueNone)
     return consumeIdent(range);
-  CSSValueList* values = CSSValueList::createSpaceSeparated();
-  while (!range.atEnd()) {
-    CSSStringValue* parsedValue = consumeString(range);
-    if (!parsedValue)
-      return nullptr;
-    values->append(*parsedValue);
-  }
-  if (values->length() && values->length() % 2 == 0)
-    return values;
-  return nullptr;
+  return consumeString(range);
 }
 
 class FontVariantLigaturesParser {
@@ -1349,33 +1340,6 @@
   return true;
 }
 
-static CSSValue* consumeTextEmphasisStyle(CSSParserTokenRange& range) {
-  CSSValueID id = range.peek().id();
-  if (id == CSSValueNone)
-    return consumeIdent(range);
-
-  if (CSSValue* textEmphasisStyle = consumeString(range))
-    return textEmphasisStyle;
-
-  CSSIdentifierValue* fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
-  CSSIdentifierValue* shape =
-      consumeIdent<CSSValueDot, CSSValueCircle, CSSValueDoubleCircle,
-                   CSSValueTriangle, CSSValueSesame>(range);
-  if (!fill)
-    fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
-  if (fill && shape) {
-    CSSValueList* parsedValues = CSSValueList::createSpaceSeparated();
-    parsedValues->append(*fill);
-    parsedValues->append(*shape);
-    return parsedValues;
-  }
-  if (fill)
-    return fill;
-  if (shape)
-    return shape;
-  return nullptr;
-}
-
 static CSSValue* consumeOutlineColor(CSSParserTokenRange& range,
                                      CSSParserMode cssParserMode) {
   // Allow the special focus color even in HTML Standard parsing mode.
@@ -3020,8 +2984,8 @@
     return cssPropertyDesc.parseSingleValue(m_range, m_context);
 
   switch (property) {
-    case CSSPropertyQuotes:
-      return consumeQuotes(m_range);
+    case CSSPropertyWebkitHighlight:
+      return consumeWebkitHighlight(m_range);
     case CSSPropertyFontVariantCaps:
       return consumeFontVariantCaps(m_range);
     case CSSPropertyFontVariantLigatures:
@@ -3213,8 +3177,6 @@
     case CSSPropertyOffsetRotate:
     case CSSPropertyOffsetRotation:
       return consumeOffsetRotate(m_range);
-    case CSSPropertyWebkitTextEmphasisStyle:
-      return consumeTextEmphasisStyle(m_range);
     case CSSPropertyOutlineColor:
       return consumeOutlineColor(m_range, m_context->mode());
     case CSSPropertyOutlineWidth:
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIQuotes.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIQuotes.cpp
new file mode 100644
index 0000000..d8fdc55
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIQuotes.cpp
@@ -0,0 +1,32 @@
+// 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 "core/css/properties/CSSPropertyAPIQuotes.h"
+
+#include "core/css/CSSStringValue.h"
+#include "core/css/CSSValueList.h"
+#include "core/css/parser/CSSParserContext.h"
+#include "core/css/parser/CSSPropertyParserHelpers.h"
+
+namespace blink {
+
+const CSSValue* CSSPropertyAPIQuotes::parseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext* context) {
+  if (range.peek().id() == CSSValueNone)
+    return CSSPropertyParserHelpers::consumeIdent(range);
+  CSSValueList* values = CSSValueList::createSpaceSeparated();
+  while (!range.atEnd()) {
+    CSSStringValue* parsedValue =
+        CSSPropertyParserHelpers::consumeString(range);
+    if (!parsedValue)
+      return nullptr;
+    values->append(*parsedValue);
+  }
+  if (values->length() && values->length() % 2 == 0)
+    return values;
+  return nullptr;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIWebkitTextEmphasisStyle.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIWebkitTextEmphasisStyle.cpp
new file mode 100644
index 0000000..8366577a
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIWebkitTextEmphasisStyle.cpp
@@ -0,0 +1,49 @@
+// 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 "core/css/properties/CSSPropertyAPIWebkitTextEmphasisStyle.h"
+
+#include "core/css/CSSStringValue.h"
+#include "core/css/CSSValueList.h"
+#include "core/css/parser/CSSPropertyParserHelpers.h"
+
+namespace blink {
+
+const CSSValue* CSSPropertyAPIWebkitTextEmphasisStyle::parseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext* context) {
+  CSSValueID id = range.peek().id();
+  if (id == CSSValueNone)
+    return CSSPropertyParserHelpers::consumeIdent(range);
+
+  if (CSSValue* textEmphasisStyle =
+          CSSPropertyParserHelpers::consumeString(range))
+    return textEmphasisStyle;
+
+  CSSIdentifierValue* fill =
+      CSSPropertyParserHelpers::consumeIdent<CSSValueFilled, CSSValueOpen>(
+          range);
+  CSSIdentifierValue* shape =
+      CSSPropertyParserHelpers::consumeIdent<CSSValueDot, CSSValueCircle,
+                                             CSSValueDoubleCircle,
+                                             CSSValueTriangle, CSSValueSesame>(
+          range);
+  if (!fill) {
+    fill = CSSPropertyParserHelpers::consumeIdent<CSSValueFilled, CSSValueOpen>(
+        range);
+  }
+  if (fill && shape) {
+    CSSValueList* parsedValues = CSSValueList::createSpaceSeparated();
+    parsedValues->append(*fill);
+    parsedValues->append(*shape);
+    return parsedValues;
+  }
+  if (fill)
+    return fill;
+  if (shape)
+    return shape;
+  return nullptr;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp b/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp
index 9d67471..246dcc0 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp
@@ -927,6 +927,7 @@
     else
       initial = true;
   }
+  DCHECK(initial ^ inherit);
 
   state.style()->removeVariable(name, isInheritedProperty);
   if (initial) {
diff --git a/third_party/WebKit/Source/core/editing/CaretBase.cpp b/third_party/WebKit/Source/core/editing/CaretBase.cpp
index 16e0f02e..144b86c 100644
--- a/third_party/WebKit/Source/core/editing/CaretBase.cpp
+++ b/third_party/WebKit/Source/core/editing/CaretBase.cpp
@@ -146,15 +146,6 @@
       node->layoutObject()->invalidatePaintRectangle(inflatedRect, this);
 }
 
-bool CaretBase::shouldRepaintCaret(Node& node) {
-  // If PositionAnchorType::BeforeAnchor or PositionAnchorType::AfterAnchor,
-  // carets need to be repainted not only when the node is contentEditable but
-  // also when its parentNode() is contentEditable.
-  node.document().updateStyleAndLayoutTree();
-  return hasEditableStyle(node) ||
-         (node.parentNode() && hasEditableStyle(*node.parentNode()));
-}
-
 void CaretBase::invalidateCaretRect(Node* node,
                                     const LayoutRect& caretLocalRect) {
   node->document().updateStyleAndLayoutTree();
diff --git a/third_party/WebKit/Source/core/editing/CaretBase.h b/third_party/WebKit/Source/core/editing/CaretBase.h
index 993371d..3984329 100644
--- a/third_party/WebKit/Source/core/editing/CaretBase.h
+++ b/third_party/WebKit/Source/core/editing/CaretBase.h
@@ -52,10 +52,6 @@
   static LayoutRect computeCaretRect(const PositionWithAffinity& caretPosition);
   static IntRect absoluteBoundsForLocalRect(Node*, const LayoutRect&);
 
-  // TODO(yosin): We should move |shouldRepaintCaret()| to "FrameCaret.cpp" as
-  // static file local function.
-  static bool shouldRepaintCaret(Node&);
-
   void paintCaret(Node*,
                   GraphicsContext&,
                   const LayoutRect& caretLocalRect,
diff --git a/third_party/WebKit/Source/core/editing/FrameCaret.cpp b/third_party/WebKit/Source/core/editing/FrameCaret.cpp
index 5e5eee8..e00c0a8 100644
--- a/third_party/WebKit/Source/core/editing/FrameCaret.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameCaret.cpp
@@ -166,6 +166,15 @@
   return caretPosition().document() == document && !caretPosition().isOrphan();
 }
 
+static bool shouldRepaintCaret(Node& node) {
+  // If PositionAnchorType::BeforeAnchor or PositionAnchorType::AfterAnchor,
+  // carets need to be repainted not only when the node is contentEditable but
+  // also when its parentNode() is contentEditable.
+  node.document().updateStyleAndLayoutTree();
+  return hasEditableStyle(node) ||
+         (node.parentNode() && hasEditableStyle(*node.parentNode()));
+}
+
 void FrameCaret::invalidateCaretRect(bool forceInvalidation) {
   if (!m_caretRectDirty)
     return;
@@ -202,11 +211,11 @@
     return;
 
   if (m_previousCaretAnchorNode &&
-      CaretBase::shouldRepaintCaret(*m_previousCaretAnchorNode)) {
+      shouldRepaintCaret(*m_previousCaretAnchorNode)) {
     m_caretBase->invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
                                           m_previousCaretRect);
   }
-  if (newAnchorNode && CaretBase::shouldRepaintCaret(*newAnchorNode))
+  if (newAnchorNode && shouldRepaintCaret(*newAnchorNode))
     m_caretBase->invalidateLocalCaretRect(newAnchorNode, newRect);
   m_previousCaretNode = newNode;
   m_previousCaretAnchorNode = newAnchorNode;
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
index b56c9bb..0c4a452a 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -1042,6 +1042,11 @@
   return m_pendingSelection->commit(layoutView);
 }
 
+void FrameSelection::didLayout() {
+  setCaretRectNeedsUpdate();
+  updateAppearance();
+}
+
 void FrameSelection::updateAppearance() {
   m_frameCaret->updateAppearance();
 
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.h b/third_party/WebKit/Source/core/editing/FrameSelection.h
index d5caa17..72a86920 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.h
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.h
@@ -215,9 +215,9 @@
   void didMergeTextNodes(const Text& oldNode, unsigned offset);
   void didSplitTextNode(const Text& oldNode);
 
+  void didLayout();
   bool isAppearanceDirty() const;
   void commitAppearanceIfNeeded(LayoutView&);
-  void updateAppearance();
   void setCaretVisible(bool caretIsVisible);
   bool isCaretBoundsDirty() const;
   void setCaretRectNeedsUpdate();
@@ -269,6 +269,7 @@
       RevealExtentOption = DoNotRevealExtent);
   void setSelectionFromNone();
 
+  void updateAppearance();
   bool shouldShowBlockCursor() const;
   void setShouldShowBlockCursor(bool);
 
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 9d35318..8dca0e8f 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -2425,8 +2425,7 @@
 
   m_postLayoutTasksTimer.stop();
 
-  m_frame->selection().setCaretRectNeedsUpdate();
-  m_frame->selection().updateAppearance();
+  m_frame->selection().didLayout();
 
   ASSERT(m_frame->document());
 
diff --git a/third_party/WebKit/Source/core/html/forms/RadioInputType.cpp b/third_party/WebKit/Source/core/html/forms/RadioInputType.cpp
index 92e36eb..ed9afd7b 100644
--- a/third_party/WebKit/Source/core/html/forms/RadioInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/RadioInputType.cpp
@@ -126,9 +126,9 @@
     }
   }
   if (inputElement) {
-    document.setFocusedElement(
-        inputElement,
-        FocusParams(SelectionBehaviorOnFocus::None, WebFocusTypeNone, nullptr));
+    document.setFocusedElement(inputElement,
+                               FocusParams(SelectionBehaviorOnFocus::Restore,
+                                           WebFocusTypeNone, nullptr));
     inputElement->dispatchSimulatedClick(event, SendNoEvents);
     event->setDefaultHandled();
     return;
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
index eaa90b37..84663769 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
@@ -463,17 +463,19 @@
     DCHECK(!m_columnSetsInvalidated);
     if (m_multiColumnSetList.isEmpty())
       return nullptr;
-    if (offset < LayoutUnit())
-      return m_multiColumnSetList.first();
+    if (offset < LayoutUnit()) {
+      columnSet = m_multiColumnSetList.first();
+    } else {
+      MultiColumnSetSearchAdapter adapter(offset);
+      m_multiColumnSetIntervalTree
+          .allOverlapsWithAdapter<MultiColumnSetSearchAdapter>(adapter);
 
-    MultiColumnSetSearchAdapter adapter(offset);
-    m_multiColumnSetIntervalTree
-        .allOverlapsWithAdapter<MultiColumnSetSearchAdapter>(adapter);
-
-    // If no set was found, the offset is in the flow thread overflow.
-    if (!adapter.result() && !m_multiColumnSetList.isEmpty())
-      return m_multiColumnSetList.last();
-    columnSet = adapter.result();
+      // If no set was found, the offset is in the flow thread overflow.
+      if (!adapter.result() && !m_multiColumnSetList.isEmpty())
+        columnSet = m_multiColumnSetList.last();
+      else
+        columnSet = adapter.result();
+    }
   }
   if (pageBoundaryRule == AssociateWithFormerPage && columnSet &&
       offset == columnSet->logicalTopInFlowThread()) {
@@ -483,7 +485,19 @@
     // previous column set.
     if (LayoutMultiColumnSet* previousSet =
             columnSet->previousSiblingMultiColumnSet())
-      return previousSet;
+      columnSet = previousSet;
+  }
+  // Avoid returning zero-height column sets, if possible. We found a column set
+  // based on a flow thread coordinate. If multiple column sets share that
+  // coordinate (because we have zero-height column sets between column
+  // spanners, for instance), look for one that has a height.
+  for (LayoutMultiColumnSet* walker = columnSet; walker;
+       walker = walker->nextSiblingMultiColumnSet()) {
+    if (!walker->isPageLogicalHeightKnown())
+      continue;
+    if (walker->logicalTopInFlowThread() == offset)
+      return walker;
+    break;
   }
   return columnSet;
 }
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 41a435f..6a3d4fd 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1122,7 +1122,6 @@
     "mhtml/MHTMLParser.h",
     "mojo/BluetoothStructTraits.cpp",
     "mojo/CommonCustomTypesStructTraits.cpp",
-    "mojo/GeometryStructTraits.cpp",
     "mojo/MojoHelper.h",
     "network/ContentSecurityPolicyParsers.cpp",
     "network/ContentSecurityPolicyParsers.h",
@@ -1513,14 +1512,6 @@
       "/wd4334",  # Result of 32-bit shift implicitly converted to 64 bits.
       "/wd4724",  # Modulo by 0.
     ]
-
-    # crbug.com/654776: Suppress symbol import warnings.
-    if (is_component_build) {
-      ldflags = [
-        "/ignore:4217",
-        "/ignore:4049",
-      ]
-    }
   } else {
     sources -= [
       "clipboard/ClipboardUtilitiesWin.cpp",
@@ -1698,6 +1689,7 @@
     "blob/BlobDataTest.cpp",
     "clipboard/ClipboardUtilitiesTest.cpp",
     "exported/FilePathConversionTest.cpp",
+    "exported/WebStringTest.cpp",
     "feature_policy/FeaturePolicyTest.cpp",
     "fonts/AcceptLanguagesResolverTest.cpp",
     "fonts/FontCacheTest.cpp",
@@ -1776,7 +1768,6 @@
     "network/LinkHeaderTest.cpp",
     "network/NetworkUtilsTest.cpp",
     "network/ResourceRequestTest.cpp",
-    "network/ResourceResponseTest.cpp",
     "network/mime/MIMETypeRegistryTest.cpp",
     "scheduler/base/intrusive_heap_unittest.cc",
     "scheduler/base/queueing_time_estimator_unittest.cc",
@@ -2089,6 +2080,7 @@
     "graphics/test/FakeGLES2Interface.h",
     "graphics/test/FakeWebGraphicsContext3DProvider.h",
     "graphics/test/MockImageDecoder.h",
+    "network/ResourceResponseTest.cpp",
   ]
 
   configs += [
diff --git a/third_party/WebKit/Source/platform/exported/WebString.cpp b/third_party/WebKit/Source/platform/exported/WebString.cpp
index 5ff32e9..a41e1b0 100644
--- a/third_party/WebKit/Source/platform/exported/WebString.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebString.cpp
@@ -38,6 +38,18 @@
 #include "wtf/text/StringView.h"
 #include "wtf/text/WTFString.h"
 
+#define STATIC_ASSERT_ENUM(a, b)                            \
+  static_assert(static_cast<int>(a) == static_cast<int>(b), \
+                "mismatching enums: " #a)
+
+STATIC_ASSERT_ENUM(WTF::LenientUTF8Conversion,
+                   blink::WebString::UTF8ConversionMode::kLenient);
+STATIC_ASSERT_ENUM(WTF::StrictUTF8Conversion,
+                   blink::WebString::UTF8ConversionMode::kStrict);
+STATIC_ASSERT_ENUM(
+    WTF::StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD,
+    blink::WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
+
 namespace blink {
 
 void WebString::reset() {
@@ -68,8 +80,9 @@
   return !m_private.isNull() && !is8Bit() ? m_private->characters16() : 0;
 }
 
-std::string WebString::utf8() const {
-  StringUTF8Adaptor utf8(m_private.get());
+std::string WebString::utf8(UTF8ConversionMode mode) const {
+  StringUTF8Adaptor utf8(m_private.get(),
+                         static_cast<WTF::UTF8ConversionMode>(mode));
   return std::string(utf8.data(), utf8.length());
 }
 
diff --git a/third_party/WebKit/Source/platform/exported/WebStringTest.cpp b/third_party/WebKit/Source/platform/exported/WebStringTest.cpp
new file mode 100644
index 0000000..c5199c4
--- /dev/null
+++ b/third_party/WebKit/Source/platform/exported/WebStringTest.cpp
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/platform/WebString.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "wtf/text/WTFString.h"
+
+namespace blink {
+
+TEST(WebStringTest, UTF8ConversionRoundTrip) {
+  // Valid characters.
+  for (UChar uchar = 0; uchar <= 0xD7FF; ++uchar) {
+    WebString utf16String(&uchar, 1);
+    std::string utf8String(utf16String.utf8());
+    WebString utf16NewString =
+        WebString::fromUTF8(utf8String.data(), utf8String.length());
+    EXPECT_FALSE(utf16NewString.isNull());
+    EXPECT_TRUE(utf16String == utf16NewString);
+  }
+
+  // Unpaired surrogates.
+  for (UChar uchar = 0xD800; uchar <= 0xDFFF; ++uchar) {
+    WebString utf16String(&uchar, 1);
+
+    // Conversion with Strict mode results in an empty string.
+    std::string utf8String(
+        utf16String.utf8(WebString::UTF8ConversionMode::kStrict));
+    EXPECT_TRUE(utf8String.empty());
+
+    // Unpaired surrogates can't be converted back in Lenient mode.
+    utf8String = utf16String.utf8(WebString::UTF8ConversionMode::kLenient);
+    EXPECT_FALSE(utf8String.empty());
+    WebString utf16NewString =
+        WebString::fromUTF8(utf8String.data(), utf8String.length());
+    EXPECT_TRUE(utf16NewString.isNull());
+
+    // Round-trip works with StrictReplacingErrorsWithFFFD mode.
+    utf8String = utf16String.utf8(
+        WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
+    EXPECT_FALSE(utf8String.empty());
+    utf16NewString =
+        WebString::fromUTF8(utf8String.data(), utf8String.length());
+    EXPECT_FALSE(utf16NewString.isNull());
+    EXPECT_FALSE(utf16String == utf16NewString);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/mojo/BUILD.gn b/third_party/WebKit/Source/platform/mojo/BUILD.gn
new file mode 100644
index 0000000..b47dfa9
--- /dev/null
+++ b/third_party/WebKit/Source/platform/mojo/BUILD.gn
@@ -0,0 +1,15 @@
+# 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.
+
+source_set("geometry_struct_traits") {
+  sources = [
+    "GeometryStructTraits.cpp",
+    "GeometryStructTraits.h",
+  ]
+  public_deps = [
+    "//third_party/WebKit/public:blink_headers",
+    "//ui/gfx/geometry",
+    "//ui/gfx/geometry/mojo:mojo_shared_cpp_sources",
+  ]
+}
diff --git a/third_party/WebKit/Source/platform/mojo/Geometry.typemap b/third_party/WebKit/Source/platform/mojo/Geometry.typemap
index 75c3545..a97b6f491d 100644
--- a/third_party/WebKit/Source/platform/mojo/Geometry.typemap
+++ b/third_party/WebKit/Source/platform/mojo/Geometry.typemap
@@ -10,6 +10,7 @@
 # Note: consumers of this typemap must themselves depend on platform.
 deps = [
   "//mojo/public/cpp/bindings",
+  "//third_party/WebKit/Source/platform/mojo:geometry_struct_traits",
 ]
 
 # TODO(zqzhang): ideally, gfx.mojom.Size should be mapped into ::blink::IntSize.
diff --git a/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.cpp b/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.cpp
index 21de7c1..4ec1c2a 100644
--- a/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.cpp
+++ b/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.cpp
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "platform/mojo/GeometryStructTraits.h"
+#include "third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h"
 
 namespace mojo {
 
 // static
-bool StructTraits<gfx::mojom::blink::Size::DataView, ::blink::WebSize>::Read(
-    gfx::mojom::blink::Size::DataView data,
+bool StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize>::Read(
+    gfx::mojom::SizeDataView data,
     ::blink::WebSize* out) {
   if (data.width() < 0 || data.height() < 0)
     return false;
diff --git a/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h b/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h
index 42afd66..b88c3b2 100644
--- a/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h
+++ b/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h
@@ -5,17 +5,16 @@
 #ifndef GeometryStructTraits_h
 #define GeometryStructTraits_h
 
-#include "platform/PlatformExport.h"
-#include "ui/gfx/geometry/mojo/geometry.mojom-blink.h"
+#include "third_party/WebKit/public/platform/WebSize.h"
+#include "ui/gfx/geometry/mojo/geometry.mojom-shared.h"
 
 namespace mojo {
 
 template <>
-struct PLATFORM_EXPORT
-    StructTraits<gfx::mojom::blink::Size::DataView, ::blink::WebSize> {
+struct StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize> {
   static int width(const ::blink::WebSize& size) { return size.width; }
   static int height(const ::blink::WebSize& size) { return size.height; }
-  static bool Read(gfx::mojom::blink::Size::DataView, ::blink::WebSize* out);
+  static bool Read(gfx::mojom::SizeDataView, ::blink::WebSize* out);
 };
 
 }  // namespace mojo
diff --git a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
index 456db9c..3ca4b69 100644
--- a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
+++ b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
@@ -703,13 +703,11 @@
   cacheControlHeader.staleWhileRevalidate =
       std::numeric_limits<double>::quiet_NaN();
 
-  DEFINE_STATIC_LOCAL(const AtomicString, noCacheDirective, ("no-cache"));
-  DEFINE_STATIC_LOCAL(const AtomicString, noStoreDirective, ("no-store"));
-  DEFINE_STATIC_LOCAL(const AtomicString, mustRevalidateDirective,
-                      ("must-revalidate"));
-  DEFINE_STATIC_LOCAL(const AtomicString, maxAgeDirective, ("max-age"));
-  DEFINE_STATIC_LOCAL(const AtomicString, staleWhileRevalidateDirective,
-                      ("stale-while-revalidate"));
+  static const char noCacheDirective[] = "no-cache";
+  static const char noStoreDirective[] = "no-store";
+  static const char mustRevalidateDirective[] = "must-revalidate";
+  static const char maxAgeDirective[] = "max-age";
+  static const char staleWhileRevalidateDirective[] = "stale-while-revalidate";
 
   if (!cacheControlValue.isEmpty()) {
     Vector<std::pair<String, String>> directives;
diff --git a/third_party/WebKit/Source/platform/network/ResourceResponse.cpp b/third_party/WebKit/Source/platform/network/ResourceResponse.cpp
index 9f58f33..810420cd 100644
--- a/third_party/WebKit/Source/platform/network/ResourceResponse.cpp
+++ b/third_party/WebKit/Source/platform/network/ResourceResponse.cpp
@@ -45,6 +45,9 @@
   return result;
 }
 
+static const char cacheControlHeader[] = "cache-control";
+static const char pragmaHeader[] = "pragma";
+
 }  // namespace
 
 ResourceResponse::SignedCertificateTimestamp::SignedCertificateTimestamp(
@@ -357,28 +360,16 @@
   return m_httpHeaderFields.get(name);
 }
 
-static const AtomicString& cacheControlHeaderString() {
-  DEFINE_STATIC_LOCAL(const AtomicString, cacheControlHeader,
-                      ("cache-control"));
-  return cacheControlHeader;
-}
-
-static const AtomicString& pragmaHeaderString() {
-  DEFINE_STATIC_LOCAL(const AtomicString, pragmaHeader, ("pragma"));
-  return pragmaHeader;
-}
-
 void ResourceResponse::updateHeaderParsedState(const AtomicString& name) {
-  DEFINE_STATIC_LOCAL(const AtomicString, ageHeader, ("age"));
-  DEFINE_STATIC_LOCAL(const AtomicString, dateHeader, ("date"));
-  DEFINE_STATIC_LOCAL(const AtomicString, expiresHeader, ("expires"));
-  DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader,
-                      ("last-modified"));
+  static const char ageHeader[] = "age";
+  static const char dateHeader[] = "date";
+  static const char expiresHeader[] = "expires";
+  static const char lastModifiedHeader[] = "last-modified";
 
   if (equalIgnoringCase(name, ageHeader))
     m_haveParsedAgeHeader = false;
-  else if (equalIgnoringCase(name, cacheControlHeaderString()) ||
-           equalIgnoringCase(name, pragmaHeaderString()))
+  else if (equalIgnoringCase(name, cacheControlHeader) ||
+           equalIgnoringCase(name, pragmaHeader))
     m_cacheControlHeader = CacheControlHeader();
   else if (equalIgnoringCase(name, dateHeader))
     m_haveParsedDateHeader = false;
@@ -440,50 +431,54 @@
 }
 
 bool ResourceResponse::cacheControlContainsNoCache() const {
-  if (!m_cacheControlHeader.parsed)
-    m_cacheControlHeader = parseCacheControlDirectives(
-        m_httpHeaderFields.get(cacheControlHeaderString()),
-        m_httpHeaderFields.get(pragmaHeaderString()));
+  if (!m_cacheControlHeader.parsed) {
+    m_cacheControlHeader =
+        parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeader),
+                                    m_httpHeaderFields.get(pragmaHeader));
+  }
   return m_cacheControlHeader.containsNoCache;
 }
 
 bool ResourceResponse::cacheControlContainsNoStore() const {
-  if (!m_cacheControlHeader.parsed)
-    m_cacheControlHeader = parseCacheControlDirectives(
-        m_httpHeaderFields.get(cacheControlHeaderString()),
-        m_httpHeaderFields.get(pragmaHeaderString()));
+  if (!m_cacheControlHeader.parsed) {
+    m_cacheControlHeader =
+        parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeader),
+                                    m_httpHeaderFields.get(pragmaHeader));
+  }
   return m_cacheControlHeader.containsNoStore;
 }
 
 bool ResourceResponse::cacheControlContainsMustRevalidate() const {
-  if (!m_cacheControlHeader.parsed)
-    m_cacheControlHeader = parseCacheControlDirectives(
-        m_httpHeaderFields.get(cacheControlHeaderString()),
-        m_httpHeaderFields.get(pragmaHeaderString()));
+  if (!m_cacheControlHeader.parsed) {
+    m_cacheControlHeader =
+        parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeader),
+                                    m_httpHeaderFields.get(pragmaHeader));
+  }
   return m_cacheControlHeader.containsMustRevalidate;
 }
 
 bool ResourceResponse::hasCacheValidatorFields() const {
-  DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader,
-                      ("last-modified"));
-  DEFINE_STATIC_LOCAL(const AtomicString, eTagHeader, ("etag"));
+  static const char lastModifiedHeader[] = "last-modified";
+  static const char eTagHeader[] = "etag";
   return !m_httpHeaderFields.get(lastModifiedHeader).isEmpty() ||
          !m_httpHeaderFields.get(eTagHeader).isEmpty();
 }
 
 double ResourceResponse::cacheControlMaxAge() const {
-  if (!m_cacheControlHeader.parsed)
-    m_cacheControlHeader = parseCacheControlDirectives(
-        m_httpHeaderFields.get(cacheControlHeaderString()),
-        m_httpHeaderFields.get(pragmaHeaderString()));
+  if (!m_cacheControlHeader.parsed) {
+    m_cacheControlHeader =
+        parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeader),
+                                    m_httpHeaderFields.get(pragmaHeader));
+  }
   return m_cacheControlHeader.maxAge;
 }
 
 double ResourceResponse::cacheControlStaleWhileRevalidate() const {
-  if (!m_cacheControlHeader.parsed)
-    m_cacheControlHeader = parseCacheControlDirectives(
-        m_httpHeaderFields.get(cacheControlHeaderString()),
-        m_httpHeaderFields.get(pragmaHeaderString()));
+  if (!m_cacheControlHeader.parsed) {
+    m_cacheControlHeader =
+        parseCacheControlDirectives(m_httpHeaderFields.get(cacheControlHeader),
+                                    m_httpHeaderFields.get(pragmaHeader));
+  }
   return m_cacheControlHeader.staleWhileRevalidate;
 }
 
@@ -504,7 +499,7 @@
 
 double ResourceResponse::date() const {
   if (!m_haveParsedDateHeader) {
-    DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("date"));
+    static const char headerName[] = "date";
     m_date = parseDateValueInHeader(m_httpHeaderFields, headerName);
     m_haveParsedDateHeader = true;
   }
@@ -513,7 +508,7 @@
 
 double ResourceResponse::age() const {
   if (!m_haveParsedAgeHeader) {
-    DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("age"));
+    static const char headerName[] = "age";
     const AtomicString& headerValue = m_httpHeaderFields.get(headerName);
     bool ok;
     m_age = headerValue.toDouble(&ok);
@@ -526,7 +521,7 @@
 
 double ResourceResponse::expires() const {
   if (!m_haveParsedExpiresHeader) {
-    DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("expires"));
+    static const char headerName[] = "expires";
     m_expires = parseDateValueInHeader(m_httpHeaderFields, headerName);
     m_haveParsedExpiresHeader = true;
   }
@@ -535,7 +530,7 @@
 
 double ResourceResponse::lastModified() const {
   if (!m_haveParsedLastModifiedHeader) {
-    DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("last-modified"));
+    static const char headerName[] = "last-modified";
     m_lastModified = parseDateValueInHeader(m_httpHeaderFields, headerName);
     m_haveParsedLastModifiedHeader = true;
   }
@@ -543,13 +538,13 @@
 }
 
 bool ResourceResponse::isAttachment() const {
-  DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("content-disposition"));
+  static const char headerName[] = "content-disposition";
+  static const char attachmentString[] = "attachment";
   String value = m_httpHeaderFields.get(headerName);
   size_t loc = value.find(';');
   if (loc != kNotFound)
     value = value.left(loc);
   value = value.stripWhiteSpace();
-  DEFINE_STATIC_LOCAL(const AtomicString, attachmentString, ("attachment"));
   return equalIgnoringCase(value, attachmentString);
 }
 
diff --git a/third_party/WebKit/Source/platform/network/ResourceResponseTest.cpp b/third_party/WebKit/Source/platform/network/ResourceResponseTest.cpp
index 4a6b6e57..573b827 100644
--- a/third_party/WebKit/Source/platform/network/ResourceResponseTest.cpp
+++ b/third_party/WebKit/Source/platform/network/ResourceResponseTest.cpp
@@ -4,10 +4,45 @@
 
 #include "platform/network/ResourceResponse.h"
 
+#include "platform/CrossThreadFunctional.h"
+#include "platform/WebTaskRunner.h"
+#include "public/platform/Platform.h"
+#include "public/platform/WebThread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
 
+namespace {
+
+ResourceResponse createTestResponse() {
+  ResourceResponse response;
+  response.addHTTPHeaderField("age", "0");
+  response.addHTTPHeaderField("cache-control", "no-cache");
+  response.addHTTPHeaderField("date", "Tue, 17 Jan 2017 04:01:00 GMT");
+  response.addHTTPHeaderField("expires", "Tue, 17 Jan 2017 04:11:00 GMT");
+  response.addHTTPHeaderField("last-modified", "Tue, 17 Jan 2017 04:00:00 GMT");
+  response.addHTTPHeaderField("pragma", "public");
+  response.addHTTPHeaderField("etag", "abc");
+  response.addHTTPHeaderField("content-disposition",
+                              "attachment; filename=a.txt");
+  return response;
+}
+
+void runHeaderRelatedTest(const ResourceResponse& response) {
+  EXPECT_EQ(0, response.age());
+  EXPECT_NE(0, response.date());
+  EXPECT_NE(0, response.expires());
+  EXPECT_NE(0, response.lastModified());
+  EXPECT_EQ(true, response.cacheControlContainsNoCache());
+}
+
+void runInThread() {
+  ResourceResponse response(createTestResponse());
+  runHeaderRelatedTest(response);
+}
+
+}  // namespace
+
 TEST(ResourceResponseTest, SignedCertificateTimestampIsolatedCopy) {
   ResourceResponse::SignedCertificateTimestamp src(
       "status", "origin", "logDescription", "logId", 7, "hashAlgorithm",
@@ -32,4 +67,16 @@
   EXPECT_NE(src.m_signatureData.impl(), dest.m_signatureData.impl());
 }
 
+TEST(ResourceResponseTest, CrossThreadAtomicStrings) {
+  // This test checks that AtomicStrings in ResourceResponse doesn't cause the
+  // failure of ThreadRestrictionVerifier check.
+  ResourceResponse response(createTestResponse());
+  runHeaderRelatedTest(response);
+  std::unique_ptr<WebThread> thread =
+      WTF::wrapUnique(Platform::current()->createThread("WorkerThread"));
+  thread->getWebTaskRunner()->postTask(BLINK_FROM_HERE,
+                                       crossThreadBind(&runInThread));
+  thread.reset();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index ebd1431..0ab358f 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -1458,6 +1458,7 @@
     WebFrameClient* client,
     WebRemoteFrame* oldWebFrame,
     WebSandboxFlags flags) {
+  DCHECK(client);
   WebLocalFrameImpl* webFrame = new WebLocalFrameImpl(oldWebFrame, client);
   Frame* oldFrame = oldWebFrame->toImplBase()->frame();
   webFrame->setParent(oldWebFrame->parent());
@@ -1471,8 +1472,7 @@
   // reuse it here.
   LocalFrame* frame = LocalFrame::create(
       webFrame->m_frameLoaderClientImpl.get(), oldFrame->host(), tempOwner,
-      client ? client->interfaceProvider() : nullptr,
-      client ? client->interfaceRegistry() : nullptr);
+      client->interfaceProvider(), client->interfaceRegistry());
   // Set the name and unique name directly, bypassing any of the normal logic
   // to calculate unique name.
   frame->tree().setPrecalculatedName(
diff --git a/third_party/WebKit/Source/wtf/text/StringUTF8Adaptor.h b/third_party/WebKit/Source/wtf/text/StringUTF8Adaptor.h
index 9eeb3ba..1aa0897 100644
--- a/third_party/WebKit/Source/wtf/text/StringUTF8Adaptor.h
+++ b/third_party/WebKit/Source/wtf/text/StringUTF8Adaptor.h
@@ -46,7 +46,9 @@
   DISALLOW_NEW();
 
  public:
-  explicit StringUTF8Adaptor(const String& string) : m_data(0), m_length(0) {
+  StringUTF8Adaptor(const String& string,
+                    UTF8ConversionMode mode = LenientUTF8Conversion)
+      : m_data(0), m_length(0) {
     if (string.isEmpty())
       return;
     // Unfortunately, 8 bit WTFStrings are encoded in Latin-1 and GURL uses
@@ -57,7 +59,7 @@
       m_data = reinterpret_cast<const char*>(string.characters8());
       m_length = string.length();
     } else {
-      m_utf8Buffer = string.utf8();
+      m_utf8Buffer = string.utf8(mode);
       m_data = m_utf8Buffer.data();
       m_length = m_utf8Buffer.length();
     }
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 08739cb..c9a387a 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -673,7 +673,6 @@
     "//device/bluetooth/public/interfaces",
     "//mojo/common:common_custom_types",
     "//ui/gfx/geometry/mojo",
-    "//ui/gfx/geometry/mojo:mojo",
     "//url/mojo:url_mojom_gurl",
     "//url/mojo:url_mojom_origin",
   ]
diff --git a/third_party/WebKit/public/platform/WebString.h b/third_party/WebKit/public/platform/WebString.h
index 5c1c540..b5e4a896c 100644
--- a/third_party/WebKit/public/platform/WebString.h
+++ b/third_party/WebKit/public/platform/WebString.h
@@ -66,6 +66,11 @@
 // * webstring.utf16()
 // * WebString::toNullableString16(webstring)
 //
+// Note that if you need to convert the UTF8 string converted from WebString
+// back to WebString with fromUTF8() you may want to specify Strict
+// UTF8ConversionMode when you call utf8(), as fromUTF8 rejects strings
+// with invalid UTF8 characters.
+//
 // Some types like GURL and base::FilePath can directly take either utf-8 or
 // utf-16 strings. Use following methods to convert WebString to/from GURL or
 // FilePath rather than going through intermediate string types:
@@ -79,6 +84,16 @@
 //
 class WebString {
  public:
+  enum class UTF8ConversionMode {
+    // Ignores errors for invalid characters.
+    kLenient,
+    // Errors out on invalid characters, returns null string.
+    kStrict,
+    // Replace invalid characters with 0xFFFD.
+    // (This is the same conversion mode as base::UTF16ToUTF8)
+    kStrictReplacingErrorsWithFFFD,
+  };
+
   ~WebString() { reset(); }
 
   WebString() {}
@@ -104,7 +119,8 @@
   bool isEmpty() const { return !length(); }
   bool isNull() const { return m_private.isNull(); }
 
-  BLINK_COMMON_EXPORT std::string utf8() const;
+  BLINK_COMMON_EXPORT std::string utf8(
+      UTF8ConversionMode = UTF8ConversionMode::kLenient) const;
 
   BLINK_COMMON_EXPORT static WebString fromUTF8(const char* data,
                                                 size_t length);
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7d4422f1..11f243c 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -59433,6 +59433,16 @@
   </summary>
 </histogram>
 
+<histogram name="ServiceWorker.ContextRequestHandlerStatus"
+    enum="ServiceWorkerContextRequestHandlerStatus">
+  <owner>falken@chromium.org</owner>
+  <summary>
+    The result of ServiceWorkerContextRequestHandler handling a request for a
+    service worker script. The histogram is suffixed to distinguish new vs
+    installed workers and main vs imported scripts.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorker.Database.DestroyDatabaseResult"
     enum="ServiceWorkerDatabaseStatus">
   <owner>nhiroki@chromium.org</owner>
@@ -103745,6 +103755,25 @@
   <int value="5" label="OpaqueRedirect"/>
 </enum>
 
+<enum name="ServiceWorkerContextRequestHandlerStatus" type="int">
+  <summary>
+    The result of ServiceWorkerContextHandler handling a request for a service
+    worker script. From ServiceWorkerContextHandler::CreateJobStatus.
+  </summary>
+  <int value="0" label="Uninitialized"/>
+  <int value="1" label="Created a write job"/>
+  <int value="2" label="Created a write job with an incumbent"/>
+  <int value="3" label="Created a read job"/>
+  <int value="4" label="Created a read job for a duplicate script import"/>
+  <int value="5" label="Error: no provider"/>
+  <int value="6" label="Error: redundant version"/>
+  <int value="7" label="Error: no context"/>
+  <int value="8" label="Error: redirect"/>
+  <int value="9"
+      label="Error: fell back to network for an uninstalled script import"/>
+  <int value="10" label="Error: out of resource ids"/>
+</enum>
+
 <enum name="ServiceWorkerDatabaseStatus" type="int">
   <int value="0" label="OK"/>
   <int value="1" label="Not Found Error"/>
@@ -116137,6 +116166,18 @@
   <affected-histogram name="interstitial.interaction"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="ServiceWorker.ContextRequestType" separator=".">
+  <suffix name="InstalledWorker.MainScript"
+      label="Main script for an installed worker"/>
+  <suffix name="InstalledWorker.ImportedScript"
+      label="Imported script for an installed worker"/>
+  <suffix name="NewWorker.MainScript"
+      label="Main script for a new (not installed) worker"/>
+  <suffix name="NewWorker.ImportedScript"
+      label="Imported script for new (not installed) worker"/>
+  <affected-histogram name="ServiceWorker.ContextRequestHandlerStatus"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="ServiceWorker.EventType">
   <suffix name="ACTIVATE" label="ACTIVATE"/>
   <suffix name="INSTALL" label="INSTALL"/>
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
index c0c6bfd..8cff994d 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
@@ -21,7 +21,7 @@
       <paper-toolbar id="toolbar">
         <div>[[filePath]]</div>
         <div id="buttons">
-           <paper-button id="open-button" on-tap="onOpenInNewButtonTap" hidden$="[[isUnsupported_(type, browsable)]]" i18n-values="aria-label:QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL" tabindex="0" has-tooltip>
+           <paper-button id="open-button" on-tap="onOpenInNewButtonTap" hidden$="[[!hasTask]]" i18n-values="aria-label:QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL" tabindex="0" has-tooltip>
              <iron-icon icon="files:open-in-new"></iron-icon>
            </paper-button>
            <files-icon-button toggles id="metadata-button" on-tap="onMetadataButtonTap_" active="{{metadataBoxActive}}" i18n-values="aria-label:QUICK_VIEW_TOGGLE_METADATA_BOX_BUTTON_LABEL" tabindex="0" has-tooltip>
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.js b/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
index c551cd52..a3096bb 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
@@ -9,6 +9,8 @@
     // File media type, e.g. image, video.
     type: String,
     filePath: String,
+    // If there is a task to open the file.
+    hasTask: Boolean,
     // URLs should be accessible from webview since contets are rendered inside
     // it. Hint: use URL.createObjectURL.
     contentUrl: String,
@@ -41,6 +43,7 @@
   clear: function() {
     this.type = '';
     this.filePath = '';
+    this.hasTask = false;
     this.contentUrl = '';
     this.videoPoster = '';
     this.audioArtwork = '';
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 86a1376..667e70aa 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -548,7 +548,7 @@
         assert(this.metadataModel_), assert(this.selectionHandler_),
         assert(this.ui_.listContainer), assert(this.quickViewModel_),
         assert(this.taskController_), fileListSelectionModel,
-        assert(this.quickViewUma_), metadataBoxController);
+        assert(this.quickViewUma_), metadataBoxController, this.dialogType);
 
     if (this.dialogType === DialogType.FULL_PAGE) {
       importer.importEnabled().then(
diff --git a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
index 7435dc6..f659b30 100644
--- a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
@@ -14,13 +14,14 @@
  * @param {!cr.ui.ListSelectionModel} fileListSelectionModel
  * @param {!QuickViewUma} quickViewUma
  * @param {!MetadataBoxController} metadataBoxController
+ * @param {DialogType} dialogType
  *
  * @constructor
  */
 function QuickViewController(
     metadataModel, selectionHandler, listContainer, quickViewModel,
     taskController, fileListSelectionModel, quickViewUma,
-    metadataBoxController) {
+    metadataBoxController, dialogType) {
   /**
    * @type {FilesQuickView}
    * @private
@@ -76,6 +77,12 @@
   this.metadataBoxController_ = metadataBoxController;
 
   /**
+   * @type {DialogType}
+   * @private
+   */
+  this.dialogType_ = dialogType;
+
+  /**
    * Current selection of selectionHandler.
    *
    * @type {!Array<!FileEntry>}
@@ -218,6 +225,17 @@
 };
 
 /**
+ * @param {!FileEntry} entry
+ * @return {!Promise<!Array<!FileTask>>}
+ * @private
+ */
+QuickViewController.prototype.getAvailableTasks_ = function(entry) {
+  return this.taskController_.getFileTasks().then(function(tasks) {
+    return tasks.getTaskItems();
+  });
+};
+
+/**
  * Update quick view using current entries.
  *
  * @return {!Promise} Promise fulfilled after quick view is updated.
@@ -238,27 +256,40 @@
       (/** @type {!FileEntry} */ (this.quickViewModel_.getSelectedEntry()));
   assert(entry);
   this.quickViewUma_.onEntryChanged(entry);
-  return this.metadataModel_.get([entry], ['thumbnailUrl', 'externalFileUrl'])
-      .then(this.onMetadataLoaded_.bind(this, entry));
+  return Promise
+      .all([
+        this.metadataModel_.get([entry], ['thumbnailUrl', 'externalFileUrl']),
+        this.getAvailableTasks_(entry)
+      ])
+      .then(function(values) {
+        var items = (/**@type{Array<MetadataItem>}*/ (values[0]));
+        var tasks = (/**@type{!Array<!FileTask>}*/ (values[1]));
+        return this.onMetadataLoaded_(entry, items, tasks);
+      }.bind(this))
+      .catch(console.error);
 };
 
 /**
- * Update quick view using file entry and loaded metadata.
+ * Update quick view using file entry and loaded metadata and tasks.
  *
  * @param {!FileEntry} entry
  * @param {Array<MetadataItem>} items
+ * @param {!Array<!FileTask>} tasks
  * @private
  */
-QuickViewController.prototype.onMetadataLoaded_ = function(entry, items) {
-  return this.getQuickViewParameters_(entry, items).then(function(params) {
-    this.quickView_.type = params.type || '';
-    this.quickView_.filePath = params.filePath || '';
-    this.quickView_.contentUrl = params.contentUrl || '';
-    this.quickView_.videoPoster = params.videoPoster || '';
-    this.quickView_.audioArtwork = params.audioArtwork || '';
-    this.quickView_.autoplay = params.autoplay || false;
-    this.quickView_.browsable = params.browsable || false;
-  }.bind(this));
+QuickViewController.prototype.onMetadataLoaded_ = function(
+    entry, items, tasks) {
+  return this.getQuickViewParameters_(entry, items, tasks)
+      .then(function(params) {
+        this.quickView_.type = params.type || '';
+        this.quickView_.filePath = params.filePath || '';
+        this.quickView_.hasTask = params.hasTask || false;
+        this.quickView_.contentUrl = params.contentUrl || '';
+        this.quickView_.videoPoster = params.videoPoster || '';
+        this.quickView_.audioArtwork = params.audioArtwork || '';
+        this.quickView_.autoplay = params.autoplay || false;
+        this.quickView_.browsable = params.browsable || false;
+      }.bind(this));
 };
 
 /**
@@ -277,11 +308,13 @@
 /**
  * @param {!FileEntry} entry
  * @param {Array<MetadataItem>} items
+ * @param {!Array<!FileTask>} tasks
  * @return !Promise<!QuickViewParams>
  *
  * @private
  */
-QuickViewController.prototype.getQuickViewParameters_ = function(entry, items) {
+QuickViewController.prototype.getQuickViewParameters_ = function(
+    entry, items, tasks) {
   var item = items[0];
   var type = FileType.getType(entry).type;
 
@@ -289,97 +322,77 @@
   var params = {
     type: type,
     filePath: entry.name,
+    hasTask: tasks.length > 0,
   };
 
-  /**
-   * @type function(!FileEntry): !Promise<!File>
-   */
-  var getFile = function(entry) {
-    return new Promise(function(resolve, reject) {
-      entry.file(resolve, reject);
-    });
-  };
-
-  if (type === 'image') {
-    if (item.externalFileUrl) {
-      if (item.thumbnailUrl) {
-        return this.loadThumbnailFromDrive_(item.thumbnailUrl)
-            .then(function(result) {
-              if (result.status === 'success')
-                params.contentUrl = result.data;
-              return params;
-            }.bind(this));
-      }
-    } else {
-      return getFile(entry).then(function(file) {
-        params.contentUrl = URL.createObjectURL(file);
-        return params;
-      });
+  if (item.externalFileUrl) {
+    switch (type) {
+      case 'image':
+        if (item.thumbnailUrl) {
+          return this.loadThumbnailFromDrive_(item.thumbnailUrl)
+              .then(function(result) {
+                if (result.status === 'success')
+                  params.contentUrl = result.data;
+                return params;
+              }.bind(this));
+        }
+        break;
+      case 'video':
+        if (item.thumbnailUrl) {
+          return this.loadThumbnailFromDrive_(item.thumbnailUrl)
+              .then(function(result) {
+                if (result.status === 'success') {
+                  params.videoPoster = result.data;
+                }
+                return params;
+              });
+        }
+        break;
     }
-  } else if (type === 'video') {
-    if (item.externalFileUrl) {
-      if (item.thumbnailUrl) {
-        return this.loadThumbnailFromDrive_(item.thumbnailUrl)
-            .then(function(result) {
-              if (result.status === 'success') {
-                params.videoPoster = result.data;
-              }
-              return params;
-            });
-      }
-    } else {
-      params.autoplay = true;
-      if (item.thumbnailUrl) {
-        params.videoPoster = item.thumbnailUrl;
-      }
-      return getFile(entry).then(function(file) {
-        params.contentUrl = URL.createObjectURL(file);
-        return params;
-      });
-    }
-  } else if (type === 'audio') {
-    if (item.externalFileUrl) {
-      // If the file is in Drive, we ask user to open it with external app.
-    } else {
-      params.autoplay = true;
-      return Promise
-          .all([
-            this.metadataModel_.get([entry], ['contentThumbnailUrl']),
-            getFile(entry)
-          ])
-          .then(function(values) {
-            /** @type {!Array<!MetadataItem>} */
-            var items = values[0];
-            /** @type {!File} */
-            var file = values[1];
-            var item = items[0];
-            if (item.contentThumbnailUrl) {
-              params.audioArtwork = item.contentThumbnailUrl;
-            }
-            params.contentUrl = URL.createObjectURL(file);
-            return params;
-          });
-    }
-  }
-  if (item.externalFileUrl || type === '.folder') {
+    // If the file is in Drive, we ask user to open it with external app.
     return Promise.resolve(params);
   }
-  return Promise
-      .all([
-        getFile(entry),
-        new Promise(function(resolve) {
-          chrome.fileManagerPrivate.getFileTasks([entry], resolve);
-        })
-      ])
-      .then(function(values) {
-        var file = values[0];
-        var tasks = values[1];
+
+  if (type === '.folder') {
+    return Promise.resolve(params);
+  }
+  return new Promise(function(resolve, reject) {
+           entry.file(resolve, reject);
+         })
+      .then(function(file) {
+        switch (type) {
+          case 'image':
+            params.contentUrl = URL.createObjectURL(file);
+            return params;
+          case 'video':
+            params.contentUrl = URL.createObjectURL(file);
+            params.autoplay = true;
+            if (item.thumbnailUrl) {
+              params.videoPoster = item.thumbnailUrl;
+            }
+            return params;
+          case 'audio':
+            params.contentUrl = URL.createObjectURL(file);
+            params.autoplay = true;
+            return this.metadataModel_.get([entry], ['contentThumbnailUrl'])
+                .then(function(items) {
+                  var item = items[0];
+                  if (item.contentThumbnailUrl) {
+                    params.audioArtwork = item.contentThumbnailUrl;
+                  }
+                  return params;
+                });
+        }
         var browsable = tasks.some(function(task) {
           return ['view-in-browser', 'view-pdf'].includes(
               task.taskId.split('|')[2]);
         });
         params.browsable = browsable;
-        params.contentUrl = browsable && URL.createObjectURL(file);
+        params.contentUrl = browsable ? URL.createObjectURL(file) : '';
+        return params;
+      }.bind(this))
+      .catch(function(e) {
+        console.error(e);
         return params;
       });
 };
diff --git a/ui/file_manager/gallery/js/gallery_data_model.js b/ui/file_manager/gallery/js/gallery_data_model.js
index 0db7823..ad2b4e5 100644
--- a/ui/file_manager/gallery/js/gallery_data_model.js
+++ b/ui/file_manager/gallery/js/gallery_data_model.js
@@ -42,14 +42,6 @@
  */
 GalleryDataModel.MAX_FULL_IMAGE_CACHE_ = 3;
 
-/**
- * Maximum number of screen size image cache.
- * @type {number}
- * @const
- * @private
- */
-GalleryDataModel.MAX_SCREEN_IMAGE_CACHE_ = 5;
-
 GalleryDataModel.prototype = {
   __proto__: cr.ui.ArrayDataModel.prototype
 };